[
  {
    "path": ".coveragerc",
    "content": "[run]\nbranch = True\nsource =\n    .\nomit =\n    .tox/*\n    /usr/*\n    setup.py\n\n[report]\nshow_missing = True\nskip_covered = True\n\nexclude_lines =\n    # Have to re-enable the standard pragma\n    \\#\\s*pragma: no cover\n\n    # Don't complain if tests don't hit defensive assertion code:\n    ^\\s*raise AssertionError\\b\n    ^\\s*raise NotImplementedError\\b\n    ^\\s*return NotImplemented\\b\n    ^\\s*raise$\n\n    # Don't complain if non-runnable code isn't run:\n    ^if __name__ == ['\"]__main__['\"]:$\n\n[html]\ndirectory = coverage-html\n\n# vim:ft=dosini\n"
  },
  {
    "path": ".deactivate.sh",
    "content": "deactivate\n"
  },
  {
    "path": ".gitignore",
    "content": "*.egg-info\n*.py[co]\n/.cache\n/.coverage\n/.tox\n/coverage-html\n/dist\n/venv\nmachine.vim\n*.swp\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "-   repo: https://github.com/pre-commit/pre-commit-hooks\n    sha: v0.9.2\n    hooks:\n    -   id: trailing-whitespace\n        language_version: python3.6\n    -   id: end-of-file-fixer\n        language_version: python3.6\n        exclude: ^\\.activate\\.sh$\n    -   id: autopep8-wrapper\n        language_version: python3.6\n    -   id: check-docstring-first\n        language_version: python3.6\n    -   id: check-executables-have-shebangs\n        language_version: python3.6\n    -   id: check-merge-conflict\n        language_version: python3.6\n    -   id: check-yaml\n        language_version: python3.6\n    -   id: debug-statements\n        language_version: python3.6\n    -   id: double-quote-string-fixer\n        language_version: python3.6\n    -   id: name-tests-test\n        language_version: python3.6\n    -   id: flake8\n        language_version: python3.6\n    -   id: check-added-large-files\n        language_version: python3.6\n        exclude: ^\\.activate\\.sh$\n    -   id: check-byte-order-marker\n        language_version: python3.6\n-   repo: https://github.com/asottile/reorder_python_imports\n    sha: v0.3.5\n    hooks:\n    -   id: reorder-python-imports\n        language_version: python3.6\n-   repo: https://github.com/asottile/pyupgrade\n    sha: v1.2.0\n    hooks:\n    -   id: pyupgrade\n        language_version: python3.6\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) [year] [fullname]\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": ".PHONY: minimal\nminimal: venv\n\nvenv:\n\ttox -e venv\n\n.PHONY: install-hooks\ninstall-hooks: venv\n\tvenv/bin/pre-commit install-hooks\n\tvenv/bin/pre-commit install\n\n.PHONY: test\ntest:\n\ttox\n\n.PHONY: clean\nclean:\n\tfind . -name '*.pyc' -delete\n\tfind . -name '__pycache__' -delete\n\trm -rf .tox\n\trm -rf venv\n\trm machine.vim\n\n.PHONY: run\nrun: venv\n\tvenv/bin/python -m vim_turing_machine.machines.merge_overlapping_intervals.merge_overlapping_intervals '[[1,2],[2,3],[5,8]]' 5\n\n.PHONY: run-vim\nbuild-vim: venv\n\tvenv/bin/python -m vim_turing_machine.machines.merge_overlapping_intervals.vim_merge_overlapping_intervals '[[1,2],[2,3],[5,8]]' 5\n\nopen-vim-machine: build-vim\n\tvim -u vimrc machine.vim\n\nrun-vim-machine: build-vim\n\tvim -u vimrc machine.vim -c ':normal gg0yy@\"'\n"
  },
  {
    "path": "README.md",
    "content": "Vim Turing Machine\n==================\n\nEver wish you could run your code in your editor? Tired of installing huge\ndependencies like bash or python to run your scripts? Love Vim so much that you\nnever want to leave it? Why not run your code... in your editor itself? Enter\nvim_turing_machine: a tool to allow you to run a Turing machine using only\nnormal mode Vim commands.\n\nAnd now you might ask, but what can we do on a Turing machine! To demonstrate\nits capabilities, we implemented a solution to the Merge Overlapping Intervals\nquestion and defined all the state transitions needed to solve this\nglorious problem. So next time you need to merge some intervals, don't\nhand-write a 10-line python program. Instead, take out your favorite editor and\nwatch it solve the problem in less than a minute with 1400 state transitions!\n\nBut a simple naysayer may say, 'We already have vimscript! Why in God's name\nwould I want to use a Turing machine instead?' To that, we retort: our Turing\nmachine only uses normal mode. So you could theoretically just type in the\nprogram and then execute it without running a single script! No ex mode either!\nThis project proves that normal mode in Vim is as powerful as any computer!\n\nMerging your favorite intervals\n===============================\n\nGiven a set of sorted potentially overlapping open/close intervals, merge the\noverlapping intervals together.\n\nExample:\n```\n[[1, 5], [6, 7]] -> [[1, 5], [6, 7]]\n[[1, 5], [2, 3], [5, 7], [12, 15]] -> [[1, 7], [12, 15]]\n```\n\nRunning the Python Turing Machine: `make run`\n\nOpening the Vim Turing Machine without running it: `make open-vim-machine`\n\nOpening and then running the Vim Turing Machine: `make run-vim-machine`\n\nSo Vim did what? Wait. How does it even?\n========================================\n\nSo you run this program, and it works. Great! So what happened? Well the most\ncommon thing you're going to see is `y$@\"`. What this does is yank from the\ncurrent cursor to the end of the line and then executes the default register as\na macro. This allows us to encode motions in lines and then execute them. We\nthen chain lines together by ending lines with moving to a mark, or a search\nresult, and then yanking and executing that line.\n\nUsing that nifty trick, we begin by yanking the first line and executing it.\nThat then sets off our mark initialization. We then search for `_<someletter>`\nand then mark that position with the corresponding letter. Generally the first\ninitial of whatever the thing we're marking is. Once everything is marked, we\nthen begin the state transitions.\n\nWe begin a state transition by executing a long command (located at `_n:`)\nwhich jumps to the tape marker, yanks it, then jumps to the current state\nmarker, yanks that too, and then searches for some transition that contains both\nthe state and tape values. Once it gets to that line, it jumps to the command\nstring and then executes our trusty `y$@\"` to execute it. To make sure we keep\ntransitioning, each state transition ends with `` `ny$@ `` which tells it to jump\nto our \"next state\" marker and then execute it again, which kicks off the search\nfor the next state.\n\nThe execution halts when it can't find a new state to transition to. The state\nsearch includes an \"or\" operator where it will fall back to matching `---`,\nwhich tells it to print the current state and halt.\n\nMost transitions themselves involve changing a tape value or a state value and\nthen moving in some direction on the tape. Changing values consists of jumping\nto the tape or state marks (`` `t `` or `` `k `` respectively) and then using\n`cw` or `C` to change the value. We then move the pointer by jumping to the tape\nposition (`` `t ``) and then moving a word forward (`W`) or backward (`B`), and\nthen marking the new tape position.\n\nThe last real piece of complication is extending the tape. We're living in a\nworld with unlimited tapes! What a time to be alive! This is done through a\nseries of nifty hacks. First, we have a modeline that sets `whichwrap+b,s`. This\nallows us to move across line breaks and keep the tape all in the screen. Next,\nthe line directly under the tape contains a \"fake\" value that, when added to our\nstate search, will prevent it from matching any real state transitions and\ninstead match a \"transition\" for adding a line to the tape. This line tells us\nto jump to the end of the tape, and then insert a full line of empty values (we\nuse `X`), and then go back to our original tape location and execute the next\nstate transition!\n\nAnd there you have it! A simple `ggyy@\"` will kick off all of these sequences\nuntil execution completes. The cool thing is that this isn't special to the\nintervals problem. In fact, you can write your own state machine and use the\nprovided Vim adapter to create a new Vim machine to solve any problem that can\nbe solved by a Turing Machine!\n\nTo see some more details about various common commands, you can take a look at\n`vim_turing_machine/vim_constants.py`. That file contains some constants that\nare used repeatedly in the generated `machine.vim` file and their names are\nfairly descriptive. Also, if you'd like to step through manually, you can edit\nthe Vim machine in `vim_turing_machine/machines/vim_is_number_even.py` and tell\nit not to auto step and then step through manually using `y$@\"`.\n\nHappy hacking!\n\nDependencies\n============\n\nTo run this code, you will need `python3.6`, `tox`, and `vim` installed on your\nmachine. This code hasn't been tested on other versions of python3, but they'll\nprobably work if you change the pinned version in `tox.ini`. This code is not\npython2 compatible.\n\nContributors\n============\n\neliot and ifij wrote this project in July 2017 for Yelp's Hackathon 23. It was inspired by [vimmmex](https://github.com/xoreaxeaxeax/vimmmex): a Brainfuck interpretor written in Vim.\n\n[modeline]: # ( vim: set fenc=utf-8 spell spl=en textwidth=80: )\n"
  },
  {
    "path": "decode_hours.sh",
    "content": "#! /bin/bash\nvenv/bin/python -m vim_turing_machine.machines.merge_overlapping_intervals.decode_intervals \"$1\" $2\n"
  },
  {
    "path": "requirements-dev.txt",
    "content": "coverage\nflake8\npre-commit>=0.15\npytest\n"
  },
  {
    "path": "setup.cfg",
    "content": "[wheel]\nuniversal = True\n"
  },
  {
    "path": "setup.py",
    "content": "from setuptools import find_packages\nfrom setuptools import setup\n\n\nsetup(\n    name='vim-turing-machine',\n    version='1.0.0',\n    classifiers=[\n        'Programming Language :: Python :: 3',\n        'Programming Language :: Python :: 3.6',\n    ],\n    install_requires=[\n        'colored',\n    ],\n    packages=find_packages(exclude=('tests*', 'testing*')),\n)\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/machines/__init__.py",
    "content": ""
  },
  {
    "path": "tests/machines/is_number_even_test.py",
    "content": "import pytest\n\nfrom vim_turing_machine.constants import BLANK_CHARACTER\nfrom vim_turing_machine.constants import NO_FINAL_STATE\nfrom vim_turing_machine.constants import YES_FINAL_STATE\nfrom vim_turing_machine.machines.is_number_even import number_is_even_state_transitions\nfrom vim_turing_machine.turing_machine import TuringMachine\n\n\ndef assert_tape(machine, expected_tape):\n    # Ignore any blanks at the end\n    assert expected_tape == ''.join(machine.tape).rstrip(BLANK_CHARACTER)\n\n\ndef run_machine(transitions, tape):\n    machine = TuringMachine(list(transitions), quiet=True)\n    machine.run(tape[:], max_steps=10000)\n\n    assert_tape(machine, tape)\n\n    return machine\n\n\n@pytest.mark.parametrize('tape, is_even', [\n    ('', False),\n    ('1', False),\n    ('0', True),\n    ('1001', False),\n    ('1010', True),\n])\ndef test_is_number_even(tape, is_even):\n    machine = run_machine(number_is_even_state_transitions, tape)\n    if is_even:\n        assert machine.current_state == YES_FINAL_STATE\n    else:\n        assert machine.current_state == NO_FINAL_STATE\n"
  },
  {
    "path": "tests/machines/merge_overlapping_intervals/__init__.py",
    "content": ""
  },
  {
    "path": "tests/machines/merge_overlapping_intervals/decode_intervals_test.py",
    "content": "from vim_turing_machine.machines.merge_overlapping_intervals.decode_intervals import decode_intervals\n\n\ndef test_encode_intervals():\n    assert decode_intervals('{}{}'.format('01010', '11111'), 5) == [[10, 31]]\n"
  },
  {
    "path": "tests/machines/merge_overlapping_intervals/encode_intervals_test.py",
    "content": "import pytest\n\nfrom vim_turing_machine.machines.merge_overlapping_intervals.encode_intervals import encode_in_x_bits\nfrom vim_turing_machine.machines.merge_overlapping_intervals.encode_intervals import encode_intervals\n\n\n@pytest.mark.parametrize('number, encoded', [\n    (0, '00000'),\n    (10, '01010'),\n    (31, '11111'),\n])\ndef test_encode_in_x_bits(number, encoded):\n    assert encode_in_x_bits(number, num_bits=5) == encoded\n\n\ndef test_encode_intervals():\n    assert encode_intervals([(10, 31)], num_bits=5) == '{}{}'.format('01010', '11111')\n"
  },
  {
    "path": "tests/machines/merge_overlapping_intervals/merge_overlapping_intervals_test.py",
    "content": "from unittest import mock\n\nimport pytest\n\nimport vim_turing_machine.machines.merge_overlapping_intervals.merge_overlapping_intervals\nimport vim_turing_machine.struct\nimport vim_turing_machine.turing_machine\nfrom vim_turing_machine.constants import INITIAL_STATE\nfrom vim_turing_machine.constants import NO_FINAL_STATE\nfrom vim_turing_machine.constants import YES_FINAL_STATE\nfrom vim_turing_machine.machines.merge_overlapping_intervals.decode_intervals import decode_intervals\nfrom vim_turing_machine.machines.merge_overlapping_intervals.encode_intervals import encode_intervals\nfrom vim_turing_machine.machines.merge_overlapping_intervals.merge_overlapping_intervals import invert_bit\nfrom vim_turing_machine.machines.merge_overlapping_intervals.merge_overlapping_intervals import invert_direction\nfrom vim_turing_machine.machines.merge_overlapping_intervals.merge_overlapping_intervals import MergeOverlappingIntervalsGenerator\nfrom vim_turing_machine.struct import BACKWARDS\nfrom vim_turing_machine.struct import FORWARDS\nfrom vim_turing_machine.turing_machine import TuringMachine\n\n\n@pytest.yield_fixture(autouse=True)\ndef mock_blank_character():\n    \"\"\"Change the blank character to be a space so that it's easier to write test cases.\"\"\"\n    with mock.patch.object(\n        vim_turing_machine.turing_machine,\n        'BLANK_CHARACTER',\n        ' ',\n    ):\n        with mock.patch.object(\n            vim_turing_machine.struct,\n            'VALID_CHARACTERS',\n            ('0', '1', ' '),\n        ):\n            with mock.patch.object(\n                vim_turing_machine.machines.merge_overlapping_intervals.merge_overlapping_intervals,\n                'BLANK_CHARACTER',\n                ' ',\n            ):\n                with mock.patch.object(\n                    vim_turing_machine.machines.merge_overlapping_intervals.merge_overlapping_intervals,\n                    'VALID_CHARACTERS',\n                    ('0', '1', ' '),\n                ):\n                    yield\n\n\n@pytest.fixture\ndef merger():\n    return MergeOverlappingIntervalsGenerator(num_bits=3)\n\n\ndef run_machine(transitions, tape, initial_position=0, assert_tape_not_changed=False):\n    machine = TuringMachine(list(transitions), quiet=True)\n    machine.run(tape[:], max_steps=10000, initial_cursor_position=initial_position)\n\n    if assert_tape_not_changed:\n        assert_tape(machine, tape)\n\n    return machine\n\n\ndef assert_cursor_at_end_of_output(machine):\n    end = len(machine.tape) - 1\n    while end > 0 and machine.tape[end] == ' ':\n        end -= 1\n\n    assert machine.cursor_position == end\n\n\ndef assert_cursor_is_at_beginning_of_input(machine):\n    i = 0\n    while i < len(machine.tape) and machine.tape[i] == ' ':\n        i += 1\n\n    assert machine.cursor_position == i\n\n\ndef assert_tape(machine, expected_tape):\n    # Ignore any blanks at the end\n    assert expected_tape == ''.join(machine.tape).rstrip()\n\n\ndef test_invert_bit():\n    assert invert_bit('0') == '1'\n    assert invert_bit('1') == '0'\n    with pytest.raises(AssertionError):\n        invert_bit('not_valid')\n\n\ndef test_invert_direction():\n    assert invert_direction(FORWARDS) == BACKWARDS\n    assert invert_direction(BACKWARDS) == FORWARDS\n    with pytest.raises(AssertionError):\n        invert_direction('not_valid')\n\n\ndef test_move_n_bits(merger):\n    machine = run_machine(\n        merger.move_n_bits(\n            initial_state=INITIAL_STATE,\n            direction=FORWARDS,\n            final_state=YES_FINAL_STATE,\n            num_bits=4,\n        ),\n        tape='01010111',\n        assert_tape_not_changed=True,\n    )\n\n    assert machine.cursor_position == 4\n\n\ndef test_move_to_blank_spaces(merger):\n    machine = run_machine(\n        merger.move_to_blank_spaces(\n            initial_state=INITIAL_STATE,\n            direction=FORWARDS,\n            final_state=YES_FINAL_STATE,\n            final_character=' ',\n            final_direction=BACKWARDS,\n            num_blanks=2,\n        ),\n        tape='01 1111 10',\n        assert_tape_not_changed=True,\n    )\n\n    assert machine.cursor_position == 6  # End of the 1111\n\n\ndef test_copy_bits_to_end_of_output(merger):\n    machine = run_machine(\n        merger.copy_bits_to_end_of_output(\n            initial_state=INITIAL_STATE,\n            num_bits=3,\n            final_state=YES_FINAL_STATE,\n        ),\n        tape='10111 01',\n    )\n\n    assert_tape(machine, '   11 01101')\n    assert_cursor_at_end_of_output(machine)\n\n\n@pytest.mark.parametrize('tape, final_state', [\n    ('101 100110', NO_FINAL_STATE),\n    ('101 100100', YES_FINAL_STATE),\n    ('101 111100', YES_FINAL_STATE),\n])\ndef test_compare_two_sequential_numbers(merger, tape, final_state):\n    machine = run_machine(\n        merger.compare_two_sequential_numbers(\n            initial_state=INITIAL_STATE,\n            greater_than_or_equal_to_state=YES_FINAL_STATE,\n            less_than_state=NO_FINAL_STATE,\n        ),\n        tape=tape,\n        initial_position=len(tape) - 1,\n        assert_tape_not_changed=True,\n    )\n\n    assert_cursor_at_end_of_output(machine)\n    assert machine.current_state == final_state\n\n\ndef test_erase_number(merger):\n    machine = run_machine(\n        merger.erase_number(\n            initial_state=INITIAL_STATE,\n            final_state=YES_FINAL_STATE,\n        ),\n        tape='100101110',\n        initial_position=5,  # end of 101\n    )\n\n    assert machine.cursor_position == 2\n    assert_tape(machine, '100   110')\n\n\ndef test_replace_number(merger):\n    tape = '100101110'\n\n    machine = run_machine(\n        merger.replace_number(\n            initial_state=INITIAL_STATE,\n            final_state=YES_FINAL_STATE,\n        ),\n        tape=tape,\n        initial_position=len(tape) - 1,\n    )\n\n    assert_tape(machine, '100110')\n    assert_cursor_at_end_of_output(machine)\n\n\n@pytest.mark.parametrize('tape, final_state', [\n    (' 100 101101', NO_FINAL_STATE),\n    ('  100101101', YES_FINAL_STATE),\n])\ndef test_check_if_there_is_any_input_left(merger, tape, final_state):\n    machine = run_machine(\n        merger.check_if_there_is_any_input_left(\n            initial_state=INITIAL_STATE,\n            final_state=NO_FINAL_STATE,  # The machine exits with Yes if there is no input left.\n        ),\n        tape=tape,\n        initial_position=len(tape) - 1,\n        assert_tape_not_changed=True,\n    )\n\n    assert_cursor_is_at_beginning_of_input(machine)\n    assert machine.current_state == final_state\n\n\n@pytest.mark.parametrize('initial_tape, final_tape', [\n    (' 100 001010001', '     001100'),  # 2nd pair's closing value is larger\n    (' 010 001110001', '     001110'),  # 2nd pair's closing value is smaller\n    (' 110 001110001', '     001110'),  # 2nd pair's closing value is equal\n])\ndef test_copy_closing_value_and_merge(merger, initial_tape, final_tape):\n    machine = run_machine(\n        merger.copy_closing_value_and_merge(\n            initial_state=INITIAL_STATE,\n            final_state=YES_FINAL_STATE,\n        ),\n        tape=initial_tape,\n        initial_position=len(initial_tape) - 1,\n    )\n\n    assert_cursor_at_end_of_output(machine)\n    assert_tape(machine, final_tape)\n\n\ndef test_copy_closing_value_without_merging(merger):\n    tape = ' 111 000010110'\n    machine = run_machine(\n        merger.copy_closing_value_without_merging(\n            initial_state=INITIAL_STATE,\n            final_state=YES_FINAL_STATE,\n        ),\n        tape=tape,\n        initial_position=len(tape) - 1,\n    )\n\n    assert_cursor_at_end_of_output(machine)\n    assert_tape(machine, '     000010110111')\n\n\n@pytest.mark.parametrize(\n    'initial_intervals, final_intervals',\n    [\n        (\n            [[0, 1]],\n            [[0, 1]],\n        ),\n        (\n            [[0, 1], [5, 6]],\n            [[0, 1], [5, 6]],\n        ),\n        (\n            [[0, 5], [2, 3]],\n            [[0, 5]],\n        ),\n        (\n            [[1, 3], [3, 4], [4, 5], [6, 7]],\n            [[1, 5], [6, 7]],\n        )\n    ]\n)\ndef test_merge_overlapping_intervals(merger, initial_intervals, final_intervals):\n    \"\"\"The true integration test!\"\"\"\n    tape = encode_intervals(initial_intervals, num_bits=3)\n    machine = run_machine(\n        merger.merge_overlapping_intervals_transitions(),\n        tape=tape,\n    )\n\n    assert final_intervals == decode_intervals(''.join(machine.tape), num_bits=3)\n"
  },
  {
    "path": "tests/machines/merge_overlapping_intervals/vim_merge_overlapping_intervals_test.py",
    "content": "import subprocess\n\nfrom vim_turing_machine.machines.merge_overlapping_intervals.decode_intervals import decode_intervals\nfrom vim_turing_machine.machines.merge_overlapping_intervals.encode_intervals import encode_intervals\nfrom vim_turing_machine.machines.merge_overlapping_intervals.merge_overlapping_intervals import MergeOverlappingIntervalsGenerator\nfrom vim_turing_machine.vim_constants import VIM_MACHINE_FILENAME\nfrom vim_turing_machine.vim_machine import VimTuringMachine\n\n\nNUM_BITS = 3\n\n\ndef run_vim_machine(intervals):\n    initial_tape = encode_intervals(intervals, NUM_BITS)\n\n    gen = MergeOverlappingIntervalsGenerator(NUM_BITS)\n    merge_overlapping_intervals = VimTuringMachine(gen.merge_overlapping_intervals_transitions(), debug=False)\n\n    # Write to the vim machine file\n    merge_overlapping_intervals.run(initial_tape=initial_tape)\n\n    subprocess.run(\n        [\n            'vim',\n            '-u',\n            'vimrc',\n            VIM_MACHINE_FILENAME,\n            '-c',\n            # Execute the vim machine and then save the resulting file\n            \":execute 'normal gg0yy@\\\"' | :x\",\n        ],\n        timeout=10,\n        check=True,\n    )\n\n\ndef read_contents_of_tape():\n    with open(VIM_MACHINE_FILENAME, 'r') as f:\n        tape_lines = []\n        found_beginning_of_tape = False\n\n        for line in f:\n            # Look for the lines between '_t:' and 'notvalid'\n            if line.startswith('_t:'):\n                found_beginning_of_tape = True\n            elif line.startswith('notvalid'):\n                return convert_tape_to_string(tape_lines)\n            elif found_beginning_of_tape:\n                tape_lines.append(line)\n\n    raise AssertionError('Could not find the tape')\n\n\ndef convert_tape_to_string(tape_lines):\n    return ''.join(tape_lines).replace(' ', '').replace('\\n', '')\n\n\ndef test_merge_intervals_in_vim():\n    run_vim_machine([[1, 2], [2, 3], [5, 7]])\n    tape = read_contents_of_tape()\n\n    intervals = decode_intervals(tape, num_bits=NUM_BITS)\n    assert intervals == [[1, 3], [5, 7]]\n"
  },
  {
    "path": "tests/turing_machine_test.py",
    "content": "import pytest\n\nfrom vim_turing_machine.constants import FORWARDS\nfrom vim_turing_machine.struct import StateTransition\nfrom vim_turing_machine.turing_machine import DuplicateStateTransitionException\nfrom vim_turing_machine.turing_machine import validate_state_transitions\n\n\ndef test_invalid_state_transition():\n    with pytest.raises(AssertionError):\n        validate_state_transitions([\n            StateTransition(\n                previous_state='foo',\n                previous_character='Not a valid character',\n                next_state='bar',\n                next_character='0',\n                tape_pointer_direction=FORWARDS,\n            )\n        ])\n\n\ndef test_valid_states():\n    validate_state_transitions(\n        [\n            StateTransition(\n                previous_state='foo',\n                previous_character='0',\n                next_state='bar',\n                next_character='0',\n                tape_pointer_direction=FORWARDS,\n            ),\n            StateTransition(\n                previous_state='foo',\n                previous_character='1',\n                next_state='bar',\n                next_character='1',\n                tape_pointer_direction=FORWARDS,\n            ),\n        ]\n    )\n\n\ndef test_duplicate_states():\n    state = StateTransition(\n        previous_state='foo',\n        previous_character='0',\n        next_state='bar',\n        next_character='0',\n        tape_pointer_direction=FORWARDS,\n    )\n    with pytest.raises(DuplicateStateTransitionException):\n        validate_state_transitions([state, state])\n"
  },
  {
    "path": "tox.ini",
    "content": "[tox]\nenvlist = py3\ntox_pip_extensions_ext_pip_custom_platform = true\ntox_pip_extensions_ext_venv_update = true\n\n[testenv]\ndeps = -rrequirements-dev.txt\npassenv = HOME SSH_AUTH_SOCK USER\ncommands =\n    coverage erase\n    coverage run -m pytest {posargs:tests}\n    coverage report\n    pre-commit install -f --install-hooks\n    pre-commit run --all-files\n\n[testenv:venv]\nbasepython = python3\nenvdir = venv\ncommands =\n\n[flake8]\nmax-line-length = 131\n\n[pep8]\nignore = E265,E309,E501\n"
  },
  {
    "path": "vim_turing_machine/__init__.py",
    "content": ""
  },
  {
    "path": "vim_turing_machine/constants.py",
    "content": "BLANK_CHARACTER = 'X'\nINITIAL_STATE = 'InitialState'\n\nYES_FINAL_STATE = 'YES'\nNO_FINAL_STATE = 'NO'\n\nBITS_PER_NUMBER = 5\n\nFINAL_STATES = [YES_FINAL_STATE, NO_FINAL_STATE]\n\n# Tape pointer direction\nFORWARDS = 1\nDO_NOT_MOVE = 0\nBACKWARDS = -1\n\nVALID_CHARACTERS = {'0', '1', BLANK_CHARACTER}\n\nINVALID_STATE_CHARACTERS = ['_', '-', ':']\n"
  },
  {
    "path": "vim_turing_machine/machines/__init__.py",
    "content": ""
  },
  {
    "path": "vim_turing_machine/machines/is_number_even.py",
    "content": "import sys\n\nfrom vim_turing_machine.constants import BLANK_CHARACTER\nfrom vim_turing_machine.constants import INITIAL_STATE\nfrom vim_turing_machine.constants import NO_FINAL_STATE\nfrom vim_turing_machine.constants import YES_FINAL_STATE\nfrom vim_turing_machine.struct import BACKWARDS\nfrom vim_turing_machine.struct import FORWARDS\nfrom vim_turing_machine.struct import StateTransition\nfrom vim_turing_machine.turing_machine import TuringMachine\n\n\nADVANCE_TO_END_OF_NUMBER = 'onward!'\nFOUND_END_OF_NUMBER = 'eof'\n\n\nnumber_is_even_state_transitions = (\n    # A number is not even if the initial tape is blank.\n    StateTransition(\n        previous_state=INITIAL_STATE,\n        previous_character=BLANK_CHARACTER,\n        next_state=NO_FINAL_STATE,\n        next_character=BLANK_CHARACTER,\n        tape_pointer_direction=FORWARDS,\n    ),\n\n    # But any other number initially means that we should go find the end of the array\n    StateTransition(\n        previous_state=INITIAL_STATE,\n        previous_character='0',\n        next_character='0',\n        next_state=ADVANCE_TO_END_OF_NUMBER,\n        tape_pointer_direction=FORWARDS,\n    ),\n    StateTransition(\n        previous_state=INITIAL_STATE,\n        previous_character='1',\n        next_character='1',\n        next_state=ADVANCE_TO_END_OF_NUMBER,\n        tape_pointer_direction=FORWARDS,\n    ),\n\n    # Once we're looking for the end of the number, go until we hit a blank\n    StateTransition(\n        previous_state=ADVANCE_TO_END_OF_NUMBER,\n        previous_character='0',\n        next_character='0',\n        next_state=ADVANCE_TO_END_OF_NUMBER,\n        tape_pointer_direction=FORWARDS,\n    ),\n    StateTransition(\n        previous_state=ADVANCE_TO_END_OF_NUMBER,\n        previous_character='1',\n        next_character='1',\n        next_state=ADVANCE_TO_END_OF_NUMBER,\n        tape_pointer_direction=FORWARDS,\n    ),\n\n    # Once we're looking for the end of the number, go until we hit a blank.\n    # Then backtrack 1 space.\n    StateTransition(\n        previous_state=ADVANCE_TO_END_OF_NUMBER,\n        previous_character=BLANK_CHARACTER,\n        next_character=BLANK_CHARACTER,\n        next_state=FOUND_END_OF_NUMBER,\n        tape_pointer_direction=BACKWARDS,\n    ),\n\n    # Now that we're on the last character, check if it is even or odd\n    StateTransition(\n        previous_state=FOUND_END_OF_NUMBER,\n        previous_character='0',\n        next_character='0',\n        next_state=YES_FINAL_STATE,\n        tape_pointer_direction=FORWARDS,\n    ),\n    StateTransition(\n        previous_state=FOUND_END_OF_NUMBER,\n        previous_character='1',\n        next_character='1',\n        next_state=NO_FINAL_STATE,\n        tape_pointer_direction=FORWARDS,\n    ),\n)\n\n\nif __name__ == '__main__':\n    even_odd_turing_machine = TuringMachine(number_is_even_state_transitions, debug=True)\n    even_odd_turing_machine.run(initial_tape=sys.argv[1])\n"
  },
  {
    "path": "vim_turing_machine/machines/merge_overlapping_intervals/__init__.py",
    "content": ""
  },
  {
    "path": "vim_turing_machine/machines/merge_overlapping_intervals/decode_intervals.py",
    "content": "\"\"\"Decodes a binary string to a json representation of the intervals\nafter the merge overlapping intervals turing machine have processed them. Reads\njson from the command line and outputs the initial tape.\"\"\"\nimport json\nimport sys\n\nfrom vim_turing_machine.constants import BITS_PER_NUMBER\nfrom vim_turing_machine.constants import BLANK_CHARACTER\n\n\ndef decode_intervals(intervals, num_bits=BITS_PER_NUMBER):\n    result = []\n    clean_intervals = intervals.replace(BLANK_CHARACTER, '').replace(' ', '')\n    index = 0\n    while index < len(clean_intervals):\n        begin = clean_intervals[index:index + num_bits]\n        begin = int(begin, 2)\n        index += num_bits\n        end = clean_intervals[index:index + num_bits]\n        end = int(end, 2)\n        index += num_bits\n        result.append([begin, end])\n\n    return result\n\n\nif __name__ == '__main__':\n    print(json.dumps(decode_intervals(sys.argv[1], int(sys.argv[2]))))\n"
  },
  {
    "path": "vim_turing_machine/machines/merge_overlapping_intervals/encode_intervals.py",
    "content": "\"\"\"Encodes a json representation of the intervals into the 5-bit binary\nrepresentation used by the merge overlapping intervals turing machine. It\ntakes input from stdin and outputs the initial tape.\"\"\"\nimport json\nimport sys\n\nfrom vim_turing_machine.constants import BITS_PER_NUMBER\n\n\ndef encode_intervals(intervals, num_bits=BITS_PER_NUMBER):\n    result = ''\n    for (begin, end) in intervals:\n        result += encode_in_x_bits(begin, num_bits)\n        result += encode_in_x_bits(end, num_bits)\n\n    return result\n\n\ndef encode_in_x_bits(number, num_bits):\n    encoded = '{:b}'.format(number)\n    assert len(encoded) <= num_bits\n\n    # Add leading zeros\n    return '0' * (num_bits - len(encoded)) + encoded\n\n\nif __name__ == '__main__':\n    print(encode_intervals(json.load(sys.stdin)))\n"
  },
  {
    "path": "vim_turing_machine/machines/merge_overlapping_intervals/merge_overlapping_intervals.py",
    "content": "\"\"\"Our tape is defined in multiple segments, each separated by a blank character.\n\nINPUT<Blank>OUTPUT\n\nThis program solves the \"Merge Overlapping Intervals\" problem: Given a sorted\nlist of possibly overlapping (opening, closing) intervals, merge them.\n\nExample: [[1, 5], [2, 3], [4, 6], [9, 11]] -> [[1, 6], [9, 11]]\n\nWe do this... on a turing machine.\n\"\"\"\nimport itertools\nimport json\nimport sys\n\nfrom vim_turing_machine.constants import BITS_PER_NUMBER\nfrom vim_turing_machine.constants import BLANK_CHARACTER\nfrom vim_turing_machine.constants import INITIAL_STATE\nfrom vim_turing_machine.constants import VALID_CHARACTERS\nfrom vim_turing_machine.constants import YES_FINAL_STATE\nfrom vim_turing_machine.machines.merge_overlapping_intervals.decode_intervals import decode_intervals\nfrom vim_turing_machine.machines.merge_overlapping_intervals.encode_intervals import encode_intervals\nfrom vim_turing_machine.struct import BACKWARDS\nfrom vim_turing_machine.struct import DO_NOT_MOVE\nfrom vim_turing_machine.struct import FORWARDS\nfrom vim_turing_machine.struct import StateTransition\nfrom vim_turing_machine.turing_machine import TuringMachine\n\n\nclass MergeOverlappingIntervalsGenerator(object):\n    def __init__(self, num_bits=BITS_PER_NUMBER):\n        self._num_bits = num_bits\n\n    def merge_overlapping_intervals_transitions(self):\n        \"\"\"This is the main orchestration point of the program\"\"\"\n        # This is the beginning of the loop that goes through the rest of the intervals.\n        CHECK_NEXT_SET_OF_HOURS = 'CheckNextSetOfHours'\n\n        # We begin the program by copying the first intervals pair into the output\n        # array. At the end of this, the cursor will be at the end of the\n        # output array.\n        transitions = list(\n            self.copy_bits_to_end_of_output(\n                initial_state=INITIAL_STATE,\n                num_bits=self._num_bits * 2,\n                final_state=CHECK_NEXT_SET_OF_HOURS,\n            )\n        )\n\n        BEGIN_COPY_NEXT_SET_OF_HOURS = 'CopyNextSetOfHours'\n        BEGIN_COMPARISON = 'BeginComparison'\n\n        # Then move back to the beginning of the input while checking if there is any input left\n        transitions.extend(\n            self.check_if_there_is_any_input_left(\n                initial_state=CHECK_NEXT_SET_OF_HOURS,\n                final_state=BEGIN_COPY_NEXT_SET_OF_HOURS,\n            )\n        )\n\n        # Now it's time to copy the opening intervals of the next pair.\n        transitions.extend(\n            self.copy_bits_to_end_of_output(\n                initial_state=BEGIN_COPY_NEXT_SET_OF_HOURS,\n                num_bits=self._num_bits,\n                final_state=BEGIN_COMPARISON,\n            )\n        )\n\n        OPEN_HOUR_IS_LESS_THAN = 'OpeningLessThan'\n        OPEN_HOUR_IS_GREATER_THAN = 'OpeningGreaterThan'\n\n        # Next we compare the closing intervals of the previous pair with the\n        # opening intervals of the current pair.\n        transitions.extend(\n            self.compare_two_sequential_numbers(\n                initial_state=BEGIN_COMPARISON,\n                greater_than_or_equal_to_state=OPEN_HOUR_IS_LESS_THAN,\n                less_than_state=OPEN_HOUR_IS_GREATER_THAN,\n            )\n        )\n\n        transitions.extend(\n            self.copy_closing_value_without_merging(\n                initial_state=OPEN_HOUR_IS_GREATER_THAN,\n                final_state=CHECK_NEXT_SET_OF_HOURS,\n            )\n        )\n\n        transitions.extend(\n            self.copy_closing_value_and_merge(\n                initial_state=OPEN_HOUR_IS_LESS_THAN,\n                final_state=CHECK_NEXT_SET_OF_HOURS,\n            )\n        )\n\n        return transitions\n\n    def copy_closing_value_without_merging(self, initial_state, final_state):\n        \"\"\"Things are super simple if we don't need to merge the intervals. We just need\n        to copy over the closing intervals from the input array.\n\n        Precondition: we are at the end of the output array. The opening intervals\n            have already been copied. The opening value is greater than the\n            previous pair's closing value.\n        Postcondition: we are at the end of the output array\n        \"\"\"\n        COPY_CLOSING_HOUR_WITHOUT_MERGING = 'CopyClosingHourWithoutMerging'\n\n        # First move back to the beginning of the input array since the copy\n        # function requires that.\n        transitions = self.move_to_blank_spaces(\n            initial_state=initial_state,\n            final_state=COPY_CLOSING_HOUR_WITHOUT_MERGING,\n            final_character=BLANK_CHARACTER,\n            final_direction=FORWARDS,\n            direction=BACKWARDS,\n            num_blanks=2,\n        )\n\n        # Then just copy the closing value from the input array to the output array.\n        transitions.extend(\n            self.copy_bits_to_end_of_output(\n                initial_state=COPY_CLOSING_HOUR_WITHOUT_MERGING,\n                num_bits=self._num_bits,\n                final_state=final_state,\n            )\n        )\n\n        return transitions\n\n    def copy_closing_value_and_merge(self, initial_state, final_state):\n        \"\"\"Call this if you need to merge in the 2nd set of intervals.\n\n        Precondition: we are at the end of the output array. The opening intervals\n            have already been copied. The opening value is less than or equal to\n            the previous pair's closing value.\n        Postcondition: we are at the end of the output array\n        \"\"\"\n        MOVE_BACK_TO_BEGINNING_TO_COPY_CLOSING_HOUR = 'MoveToBeginningToCopyClosingHour'\n        COPY_OVER_CLOSING_HOUR = 'CopyClosingHour'\n        COMPARE_CLOSING_HOUR = 'CompareClosingHour'\n        CLOSING_HOUR_IS_LARGER = 'ClosingHourIsLarger'\n        CLOSING_HOUR_IS_NOT_LARGER = 'ClosingHourIsNotLarger'\n\n        # If the opening value is less than the closing value of the previous pair,\n        # then we discard that opening value. So essentially, [2, 7, 5] becomes [2, 7].\n        transitions = self.erase_number(\n            initial_state=initial_state,\n            final_state=MOVE_BACK_TO_BEGINNING_TO_COPY_CLOSING_HOUR,\n        )\n\n        # Move back to the beginning of the array.\n        transitions.extend(\n            self.move_to_blank_spaces(\n                initial_state=MOVE_BACK_TO_BEGINNING_TO_COPY_CLOSING_HOUR,\n                final_state=COPY_OVER_CLOSING_HOUR,\n                final_character=BLANK_CHARACTER,\n                final_direction=FORWARDS,\n                direction=BACKWARDS,\n                num_blanks=2,\n            )\n        )\n\n        # Now after erasing that number, we need to copy over the closing value so\n        # that we can merge it in.\n        transitions.extend(\n            self.copy_bits_to_end_of_output(\n                initial_state=COPY_OVER_CLOSING_HOUR,\n                num_bits=self._num_bits,\n                final_state=COMPARE_CLOSING_HOUR,\n            )\n        )\n\n        # Now we take the max of the 2 pairs' closing intervals.\n        transitions.extend(\n            self.compare_two_sequential_numbers(\n                initial_state=COMPARE_CLOSING_HOUR,\n                less_than_state=CLOSING_HOUR_IS_LARGER,\n                greater_than_or_equal_to_state=CLOSING_HOUR_IS_NOT_LARGER,\n            )\n        )\n\n        # If the closing value is less than or equal to the previous closing value, just nuke it.\n        transitions.extend(\n            self.erase_number(\n                initial_state=CLOSING_HOUR_IS_NOT_LARGER,\n                final_state=final_state,\n            )\n        )\n\n        # But if the closing value is greater than the previous closing value, we\n        # should overwrite that closing value with our larger value.\n        transitions.extend(\n            self.replace_number(\n                initial_state=CLOSING_HOUR_IS_LARGER,\n                final_state=final_state,\n            )\n        )\n\n        return transitions\n\n    def noop_when_non_blank(self, state, direction):\n        return (\n            StateTransition(\n                previous_state=state,\n                previous_character='0',\n                next_state=state,\n                next_character='0',\n                tape_pointer_direction=direction,\n            ),\n            StateTransition(\n                previous_state=state,\n                previous_character='1',\n                next_state=state,\n                next_character='1',\n                tape_pointer_direction=direction,\n            ),\n        )\n\n    def move_n_bits(self, initial_state, direction, final_state, num_bits):\n        \"\"\"Moves 'num_bits' in the specified direction. Errors if it encounters a\n        blank space while doing so. Ends in the final_state.\"\"\"\n        def state_name(bit_index):\n            if bit_index == 0:\n                return initial_state\n            elif bit_index == num_bits:\n                return final_state\n            else:\n                return '{}MovingBit{}'.format(initial_state, bit_index)\n\n        return itertools.chain.from_iterable([\n            [\n                StateTransition(\n                    previous_state=state_name(bit_index),\n                    previous_character=bit_value,\n                    next_state=state_name(bit_index + 1),\n                    next_character=bit_value,\n                    tape_pointer_direction=direction,\n                )\n                for bit_index in range(num_bits)\n            ]\n            for bit_value in ['0', '1']\n        ])\n\n    def move_to_blank_spaces(\n        self,\n        initial_state,\n        final_state,\n        final_character,\n        final_direction,\n        direction,\n        num_blanks,\n    ):\n        \"\"\"Moves along the array until it hits a certain number of blank spaces.\n\n        :param str initial_state: The state used to trigger this code\n        :param str final_state: The state we should finish with\n        :param str final_character: The character we should write on that state transition\n        :param int final_direction: Which direction we should move at the end\n        :param int direction: Which direction we should search in\n        :param int num_blanks: How many blanks to search for\n        \"\"\"\n\n        def state_name(blank_num):\n            return '{}Searching{}'.format(initial_state, blank_num)\n\n        transitions = [\n            # Rename our current state\n            StateTransition(\n                previous_state=initial_state,\n                previous_character=character,\n                next_state=state_name(blank_num=0),\n                next_character=character,\n                tape_pointer_direction=DO_NOT_MOVE,\n            )\n            for character in VALID_CHARACTERS\n        ]\n\n        for blank_num in range(num_blanks):\n            transitions.extend(\n                # If we're looking for the first blank, then keep going until we hit it\n                self.noop_when_non_blank(state_name(blank_num=blank_num), direction=direction)\n            )\n\n            if blank_num == num_blanks - 1:\n                # This is the last blank\n                transitions.append(\n                    StateTransition(\n                        previous_state=state_name(blank_num),\n                        previous_character=BLANK_CHARACTER,\n                        next_state=final_state,\n                        next_character=final_character,\n                        tape_pointer_direction=final_direction,\n                    )\n                )\n            else:\n                # This is not the last blank\n                transitions.append(\n                    StateTransition(\n                        previous_state=state_name(blank_num),\n                        previous_character=BLANK_CHARACTER,\n                        next_state=state_name(blank_num + 1),\n                        next_character=BLANK_CHARACTER,\n                        tape_pointer_direction=direction,\n                    )\n                )\n\n        return transitions\n\n    def copy_bits_to_end_of_output(self, initial_state, num_bits, final_state):\n        \"\"\"\n        :param string initial_state: The state used before we start to move\n        :param int num_bits: The number of bits to copy\n        :param StateTransition final_state: The state to finish with when we are done copying\n\n        Note: This overwrites the copied section with blanks.\n\n        Precondition: We are at the beginning of the input array\n        Postcondition: We are at the end of the output array\n\n        :rtype: [StateTransition]\n        \"\"\"\n        def state_name(bit_index):\n            if bit_index == 0:\n                return initial_state\n            else:\n                return '{}Copy{}'.format(initial_state, bit_index)\n\n        def copy_bit(bit_index, bit_value):\n            base_copying_state = '{}Bit{}'.format(state_name(bit_index + 1), bit_value)\n\n            return [\n                # Let's start copying the character. Note how we replace it with a blank.\n                StateTransition(\n                    previous_state=state_name(bit_index),\n                    previous_character=bit_value,\n                    next_state='{}Forward'.format(base_copying_state),\n                    next_character=BLANK_CHARACTER,\n                    tape_pointer_direction=FORWARDS,\n                ),\n\n                *self.move_to_blank_spaces(\n                    initial_state='{}Forward'.format(base_copying_state),\n                    # If we're on the last character, don't go backwards\n                    final_state=(\n                        '{}Backwards'.format(base_copying_state)\n                        if bit_index < num_bits - 1\n                        else final_state\n                    ),\n                    final_character=bit_value,\n                    final_direction=DO_NOT_MOVE,\n                    direction=FORWARDS,\n                    num_blanks=2,\n                ),\n                *self.move_to_blank_spaces(\n                    initial_state='{}Backwards'.format(base_copying_state),\n                    final_state=state_name(bit_index + 1),\n                    final_character=BLANK_CHARACTER,\n                    final_direction=FORWARDS,\n                    direction=BACKWARDS,\n                    num_blanks=2,\n                ),\n            ]\n\n        return itertools.chain.from_iterable(\n            (\n                *copy_bit(bit_index, bit_value='0'),\n                *copy_bit(bit_index, bit_value='1'),\n            )\n            for bit_index in range(num_bits)\n        )\n\n    def compare_two_sequential_numbers(self, initial_state, greater_than_or_equal_to_state, less_than_state):\n        \"\"\"\n        If the earlier number is greater than or equal to the later number, this\n        will end in the greater_than_or_equal_to_state. If the earlier number is\n        less than the later number, this will end in the less_than_state.\n\n        Precondition: The cursor is at the end of the output array\n        Postcondition: The cursor is at the end of the output array\n        \"\"\"\n        # We can't directly transition into the >= or < states since we need to end\n        # up at the end of the output array.\n        FOUND_GREATER_THAN_OR_EQUAL_TO_STATE = '{}FoundGreaterThanOrEqualTo'.format(initial_state)\n        FOUND_LESS_THAN_STATE = '{}FoundLessThan'.format(initial_state)\n\n        def already_have_one_bit_state(bit_index, bit_value):\n            \"\"\"This means that we've read a 'bit_value' at 'bit_index'. We are\n            currently searching for the equivalent bit in the other number.\"\"\"\n            return '{}BitIndex{}BitValue{}'.format(initial_state, bit_index, bit_value)\n\n        def about_to_read_first_bit_state(bit_index):\n            \"\"\"This means that we're about to start reading/comparing the next bit\n            index. We always immediately transition from this state to the\n            'already_have_one_bit_state' after reading its value.\"\"\"\n            if bit_index == self._num_bits:\n                # At this point, we know that the numbers are equal since we've compared every bit.\n                return FOUND_GREATER_THAN_OR_EQUAL_TO_STATE\n            else:\n                return '{}BitIndex{}'.format(initial_state, bit_index)\n\n        def about_to_compare_bits_state(bit_index, bit_value):\n            \"\"\"Our cursor is over the other bit we want to compare this one too.\"\"\"\n            return '{}BitIndex{}CompareWithBitValue{}'.format(initial_state, bit_index, bit_value)\n\n        # Begin by moving to the beginning of the 2nd number.\n        transitions = list(\n            self.move_n_bits(\n                initial_state=initial_state,\n                direction=BACKWARDS,\n                final_state=about_to_read_first_bit_state(bit_index=0),\n                num_bits=self._num_bits - 1,\n            )\n        )\n\n        direction = BACKWARDS\n\n        # Then begin comparing the digits one by one from largest to smallest\n        for bit_index in range(self._num_bits):\n            for bit_value in ['0', '1']:\n                transitions.append(\n                    # Read the current bit\n                    StateTransition(\n                        previous_state=about_to_read_first_bit_state(bit_index),\n                        previous_character=bit_value,\n                        next_state=already_have_one_bit_state(bit_index, bit_value),\n                        next_character=bit_value,\n                        tape_pointer_direction=direction,\n                    )\n                )\n\n                # Then go to the equivalent bit in the other number. We already\n                # moved 1 space in that direction.\n                transitions.extend(\n                    self.move_n_bits(\n                        initial_state=already_have_one_bit_state(bit_index, bit_value),\n                        direction=direction,\n                        final_state=about_to_compare_bits_state(bit_index, bit_value),\n                        num_bits=self._num_bits - 1,\n                    )\n                )\n\n                # We've already read the 2nd number and now we're comparing it to\n                # the first. Now finally do the comparison\n                transitions.append(\n                    # If the numbers are equal\n                    StateTransition(\n                        previous_state=about_to_compare_bits_state(bit_index, bit_value),\n                        previous_character=bit_value,\n                        next_state=about_to_read_first_bit_state(bit_index + 1),\n                        next_character=bit_value,\n                        tape_pointer_direction=FORWARDS,\n                    )\n                )\n\n                transitions.append(\n                    # If the numbers are not equal\n                    StateTransition(\n                        previous_state=about_to_compare_bits_state(bit_index, bit_value),\n                        previous_character=invert_bit(bit_value),\n                        next_state=(\n                            FOUND_GREATER_THAN_OR_EQUAL_TO_STATE\n                            if (\n                                (bit_value == '1' and direction == FORWARDS) or\n                                (bit_value == '0' and direction == BACKWARDS)\n                            )\n                            else FOUND_LESS_THAN_STATE\n                        ),\n                        next_character=invert_bit(bit_value),\n                        tape_pointer_direction=invert_direction(direction),\n                    )\n                )\n\n            direction = invert_direction(direction)\n\n        # After we've determined the answer, we need to move to the end of the output array\n        transitions.extend(\n            self.move_to_blank_spaces(\n                initial_state=FOUND_GREATER_THAN_OR_EQUAL_TO_STATE,\n                final_state=greater_than_or_equal_to_state,\n                final_character=BLANK_CHARACTER,\n                final_direction=BACKWARDS,\n                direction=FORWARDS,\n                num_blanks=1,\n            )\n        )\n\n        transitions.extend(\n            self.move_to_blank_spaces(\n                initial_state=FOUND_LESS_THAN_STATE,\n                final_state=less_than_state,\n                final_character=BLANK_CHARACTER,\n                final_direction=BACKWARDS,\n                direction=FORWARDS,\n                num_blanks=1,\n            )\n        )\n\n        return transitions\n\n    def erase_number(self, initial_state, final_state):\n        \"\"\"Erases the number under the cursor by replacing it with blanks.\n\n        Precondition: The cursor is at the end of that number\n        Postcondition: The cursor is right before the beginning of that number\n        \"\"\"\n        def state_name(bit_index):\n            if bit_index == 0:\n                return initial_state\n            elif bit_index == self._num_bits:\n                return final_state\n            else:\n                return '{}ErasingBit{}'.format(initial_state, bit_index)\n\n        transitions = []\n\n        for bit_index in range(self._num_bits):\n            for bit_value in ['0', '1']:\n                transitions.append(\n                    StateTransition(\n                        previous_state=state_name(bit_index),\n                        previous_character=bit_value,\n                        next_state=state_name(bit_index + 1),\n                        next_character=BLANK_CHARACTER,\n                        tape_pointer_direction=BACKWARDS,\n                    )\n                )\n\n        return transitions\n\n    def replace_number(self, initial_state, final_state):\n        \"\"\"Replaces the 2nd to last number with the last number. So, [1, 5, 7] would become [1, 7].\n\n        Precondition: The cursor is at the end of the output array\n        Postcondition: The cursor is at the end of the output array\n        \"\"\"\n        def need_to_read_bit(bit_index):\n            if bit_index == 0:\n                return initial_state\n            elif bit_index == self._num_bits:\n                return final_state\n            else:\n                return '{}ReadingBitIndexToMove{}'.format(initial_state, bit_index)\n\n        def read_bit(bit_index, bit_value):\n            return '{}ReadingBitIndexToMove{}Bit{}'.format(initial_state, bit_index, bit_value)\n\n        def overwrite_bit(bit_index, bit_value):\n            return '{}OverwritingBitIndex{}Bit{}'.format(initial_state, bit_index, bit_value)\n\n        def move_back_to_end(bit_index):\n            return '{}ReplacingNumberMovingBackToEnd{}'.format(initial_state, bit_index)\n\n        transitions = []\n        for bit_index in range(self._num_bits):\n            for bit_value in ['0', '1']:\n                # Start by reading the bit under the cursor. Replace it with a blank.\n                transitions.append(\n                    StateTransition(\n                        previous_state=need_to_read_bit(bit_index),\n                        previous_character=bit_value,\n                        next_state=read_bit(bit_index, bit_value),\n                        next_character=BLANK_CHARACTER,\n                        tape_pointer_direction=BACKWARDS,\n                    )\n                )\n\n                # Then go to the equivalent bit in the other number.\n                transitions.extend(\n                    self.move_n_bits(\n                        initial_state=read_bit(bit_index, bit_value),\n                        direction=BACKWARDS,\n                        final_state=overwrite_bit(bit_index, bit_value),\n                        num_bits=self._num_bits - 1,\n                    )\n                )\n\n                # Then overwrite the current bit with the stored bit\n                transitions.extend(\n                    StateTransition(\n                        previous_state=overwrite_bit(bit_index, bit_value),\n                        previous_character=bit_value_we_are_reading,\n                        next_state=move_back_to_end(bit_index),\n                        next_character=bit_value,\n                        tape_pointer_direction=FORWARDS,\n                    )\n                    for bit_value_we_are_reading in ['0', '1']\n                )\n\n            # Lastly move back to the end of the output array\n            transitions.extend(\n                self.move_to_blank_spaces(\n                    initial_state=move_back_to_end(bit_index),\n                    final_state=need_to_read_bit(bit_index + 1),\n                    final_character=BLANK_CHARACTER,\n                    final_direction=BACKWARDS,\n                    direction=FORWARDS,\n                    num_blanks=1,\n                )\n            )\n\n        return transitions\n\n    def check_if_there_is_any_input_left(self, initial_state, final_state):\n        \"\"\"\n        Precondition: We are at the end of the output array\n        Postcondition: We are at the beginning of the input array\n\n        If there is no more input left, this ends the program\n        \"\"\"\n        CHECK_IF_ANY_HOURS_LEFT = '{}CheckIfAnyHoursLeft'.format(initial_state)\n\n        # Then move back to the beginning of the input\n        transitions = self.move_to_blank_spaces(\n            initial_state=initial_state,\n            final_state=CHECK_IF_ANY_HOURS_LEFT,\n            final_character=BLANK_CHARACTER,\n            final_direction=FORWARDS,\n            direction=BACKWARDS,\n            num_blanks=2,\n        )\n\n        # If we moved back 2 blanks and still ended on a blank, then there is\n        # nothing left in the input because we hit 2 blanks in a row.\n        transitions.append(\n            StateTransition(\n                previous_state=CHECK_IF_ANY_HOURS_LEFT,\n                previous_character=BLANK_CHARACTER,\n                next_state=YES_FINAL_STATE,\n                next_character=BLANK_CHARACTER,\n                tape_pointer_direction=FORWARDS,\n            )\n        )\n\n        # If we did not find a blank, then we just chill where we are\n        transitions.extend(\n            StateTransition(\n                previous_state=CHECK_IF_ANY_HOURS_LEFT,\n                previous_character=bit_value,\n                next_state=final_state,\n                next_character=bit_value,\n                tape_pointer_direction=DO_NOT_MOVE,\n            )\n            for bit_value in ['0', '1']\n        )\n\n        return transitions\n\n\ndef invert_bit(bit_value):\n    if bit_value == '0':\n        return '1'\n    elif bit_value == '1':\n        return '0'\n    else:\n        raise AssertionError('Invalid bit {}'.format(bit_value))\n\n\ndef invert_direction(direction):\n    if direction == BACKWARDS:\n        return FORWARDS\n    elif direction == FORWARDS:\n        return BACKWARDS\n    else:  # pragma: no cover\n        raise AssertionError('Invalid direction {}'.format(direction))\n\n\nif __name__ == '__main__':\n    input_string = json.loads(sys.argv[1])\n    num_bits = int(sys.argv[2])\n\n    initial_tape = encode_intervals(input_string, num_bits)\n\n    gen = MergeOverlappingIntervalsGenerator(num_bits)\n    merge_overlapping_intervals = TuringMachine(gen.merge_overlapping_intervals_transitions(), debug=True)\n    merge_overlapping_intervals.run(initial_tape=initial_tape, max_steps=5000)\n\n    print(decode_intervals(''.join(merge_overlapping_intervals.tape), num_bits))\n"
  },
  {
    "path": "vim_turing_machine/machines/merge_overlapping_intervals/vim_merge_overlapping_intervals.py",
    "content": "import json\nimport sys\n\nfrom vim_turing_machine.machines.merge_overlapping_intervals.encode_intervals import encode_intervals\nfrom vim_turing_machine.machines.merge_overlapping_intervals.merge_overlapping_intervals import MergeOverlappingIntervalsGenerator\nfrom vim_turing_machine.vim_machine import VimTuringMachine\n\n\nif __name__ == '__main__':\n    input_string = json.loads(sys.argv[1])\n    num_bits = int(sys.argv[2])\n\n    initial_tape = encode_intervals(input_string, num_bits)\n\n    gen = MergeOverlappingIntervalsGenerator(num_bits)\n    merge_overlapping_intervals = VimTuringMachine(gen.merge_overlapping_intervals_transitions(), debug=False)\n    merge_overlapping_intervals.run(initial_tape=initial_tape)\n"
  },
  {
    "path": "vim_turing_machine/machines/vim_is_number_even.py",
    "content": "import sys\n\nfrom vim_turing_machine.machines.is_number_even import number_is_even_state_transitions\nfrom vim_turing_machine.vim_machine import VimTuringMachine\n\nif __name__ == '__main__':\n    initial_tape = sys.argv[1]\n\n    even_odd_turing_machine = VimTuringMachine(number_is_even_state_transitions, debug=False)\n    even_odd_turing_machine.run(initial_tape=initial_tape)\n"
  },
  {
    "path": "vim_turing_machine/struct.py",
    "content": "from collections import namedtuple\n\nfrom vim_turing_machine.constants import BACKWARDS\nfrom vim_turing_machine.constants import DO_NOT_MOVE\nfrom vim_turing_machine.constants import FORWARDS\nfrom vim_turing_machine.constants import INVALID_STATE_CHARACTERS\nfrom vim_turing_machine.constants import VALID_CHARACTERS\n\n\nclass StateTransition(namedtuple('StateTransition', [\n    'previous_state',\n    'previous_character',\n    'next_state',\n    'next_character',\n    'tape_pointer_direction',\n])):\n    def validate(self):\n        assert self.tape_pointer_direction in (FORWARDS, DO_NOT_MOVE, BACKWARDS)\n        assert self.previous_character in VALID_CHARACTERS\n        assert self.next_character in VALID_CHARACTERS\n        for invalid_char in INVALID_STATE_CHARACTERS:\n            if invalid_char in self.previous_state:\n                raise AssertionError('{} is in {}'.format(invalid_char, self.previous_state))\n\n            if invalid_char in self.next_state:\n                raise AssertionError('{} is in {}'.format(invalid_char, self.next_state))\n"
  },
  {
    "path": "vim_turing_machine/turing_machine.py",
    "content": "from collections import defaultdict\n\nimport colored\n\nfrom vim_turing_machine.constants import BLANK_CHARACTER\nfrom vim_turing_machine.constants import FINAL_STATES\nfrom vim_turing_machine.constants import INITIAL_STATE\n\n\nclass NegativeTapePositionException(Exception):\n    pass\n\n\nclass MissingStateTransition(Exception):\n    pass\n\n\nclass DuplicateStateTransitionException(Exception):\n    pass\n\n\nclass TooManyStepsException(Exception):\n    pass\n\n\nclass TuringMachine(object):\n\n    def __init__(self, state_transitions, debug=False, quiet=False):\n        validate_state_transitions(state_transitions)\n\n        self._state_transitions = state_transitions\n        self._state_transition_mapping = {\n            (state.previous_state, state.previous_character): state\n            for state in state_transitions\n        }\n        self._debug = debug\n        self._quiet = quiet\n        self.initialize_machine(tape=[])\n\n    def initialize_machine(self, tape, initial_cursor_position=0):\n        if tape:\n            self.tape = list(tape)[:]  # Copy the initial tape since we mutate it\n        else:\n            self.tape = [BLANK_CHARACTER]\n\n        self.cursor_position = initial_cursor_position\n        self.current_state = INITIAL_STATE\n        self._num_steps = 0\n\n    def get_state_transition(self):\n        try:\n            return self._state_transition_mapping[\n                (self.current_state, self.tape[self.cursor_position])\n            ]\n        except KeyError:\n            raise MissingStateTransition(\n                (self.current_state, self.tape[self.cursor_position])\n            )\n\n    def step(self):\n        \"\"\"This implements an infinitely long tape in the right direction, but\n        will error if you go beyond position 0\"\"\"\n        transition = self.get_state_transition()\n\n        self.tape[self.cursor_position] = transition.next_character\n\n        self.cursor_position += transition.tape_pointer_direction\n\n        if self.cursor_position < 0:\n            raise NegativeTapePositionException\n\n        # Make sure we haven't run more than 1 past the end of the tape. This\n        # should never happen since we append to the tape over time.\n        assert self.cursor_position <= len(self.tape)\n\n        if self.cursor_position >= len(self.tape):\n            # Fake the infinite tape by adding a blank character under the\n            # cursor.\n            self.tape.append(BLANK_CHARACTER)\n\n        self.current_state = transition.next_state\n\n        if self.current_state in FINAL_STATES:\n            self.final_state()\n        elif self._debug:\n            self.print_tape()\n\n    def final_state(self):\n        if not self._quiet:\n            print('Program complete. Final state: {}'.format(self.current_state))\n            print(\n                'The program completed in {} steps using a machine with {} transitions'.format(\n                    self._num_steps,\n                    len(self._state_transitions)\n                )\n            )\n            self.print_tape()\n\n        raise StopIteration\n\n    def run(self, initial_tape, max_steps=None, initial_cursor_position=0):\n        self.initialize_machine(initial_tape, initial_cursor_position=initial_cursor_position)\n\n        if self._debug:\n            self.print_tape()\n\n        try:\n            while(True):\n                self.step()\n                self._num_steps += 1\n\n                if max_steps is not None and self._num_steps >= max_steps:\n                    raise TooManyStepsException\n        except StopIteration:\n            pass\n\n    def print_tape(self):\n        tape = ''\n        for i, character in enumerate(self.tape):\n            if i == self.cursor_position:\n                tape += '{}{}{}{}'.format(colored.bg('red'), colored.fg('white'), character, colored.attr('reset'))\n            else:\n                tape += character\n\n            if i != len(self.tape) - 1:\n                tape += ' | '\n\n        print(tape)\n        print('State: {}'.format(self.current_state))\n        print()  # Add empty line\n\n\ndef validate_state_transitions(state_transitions):\n    seen = defaultdict(list)\n\n    for transition in state_transitions:\n        transition.validate()\n\n        key = (transition.previous_state, transition.previous_character)\n        seen[key].append(transition)\n\n    for transitions in seen.values():\n        if len(transitions) > 1:\n            raise DuplicateStateTransitionException(transitions)\n"
  },
  {
    "path": "vim_turing_machine/vim_constants.py",
    "content": "VIM_MACHINE_FILENAME = 'machine.vim'\n\nVIM_NEXT_STATE = '`ny$@\"'\n\nVIM_MOVE_TAPE_FORWARDS = '`tWmt'\n\nVIM_MOVE_TAPE_BACKWARDS = '`tBmt'\n\nVIM_RUN_REGISTER = '@\"'\n\nVIM_TAPE_WRAP_POSITION = 40\n\nVIM_LOG_TAPE_AND_STATE = '`ly$@\"'\n\n\ndef create_pointer(name, direction='j'):\n    \"\"\"Creates a mark to a particular place on the tape.\"\"\"\n    return '`h/_{name}:\\rn{direction}m{name}'.format(name=name, direction=direction)\n\n\nVIM_POINTERS = ''.join([\n    create_pointer('t'),\n    create_pointer('l'),\n    create_pointer('k'),\n    create_pointer('o'),\n    create_pointer('p'),\n    create_pointer('s', direction=''),\n    create_pointer('n'),\n    create_pointer('e', direction='k'),\n])\n\nVIM_TEMPLATE = \"\"\"0/_v1\\rnf-ly$@\"\n\n### launch with ggyy@\" ###\n\n# Init pointers\n_v1-gg0mh{pointers}`ny$@\"\n\n_o:  # Output\n\n\n_k:  # Current state\n{initial_state}\n\n_t:  # Current tape\n{initial_tape}\nnotvalid\\|--addlinetotape\n_e:  # End of tape. Pointer is 1 line above this\n\n_n:  # Next state transition. Usage: `ny$@\"\n{logging}`t\"tyiW`ky$`s/_\u0012\"-\u0012t\\|---\\rf:ly$@\"\n\n_p:  # Print state. Usage: `py$@\"\n`ky$`op\n\n_l:  # Log the tape and state Usage: `ly$@\"\n`tyipGo\u001bpdd`kyyGp\n\n_s:  # State transitions\n{state_transitions}\n# End State transitions\n# Add an extra line to the end of the tape\n_--addlinetotape: `eO\u001b{characters_per_line}i{blank_character} \u001b0mt`ny$@\"\n\n# Print state when unknown transition\n_---: `py$@\"\n\n# vim: set whichwrap+=b,s\n\"\"\"\n"
  },
  {
    "path": "vim_turing_machine/vim_machine.py",
    "content": "from vim_turing_machine.constants import BACKWARDS\nfrom vim_turing_machine.constants import BLANK_CHARACTER\nfrom vim_turing_machine.constants import FORWARDS\nfrom vim_turing_machine.turing_machine import TuringMachine\nfrom vim_turing_machine.vim_constants import VIM_LOG_TAPE_AND_STATE\nfrom vim_turing_machine.vim_constants import VIM_MACHINE_FILENAME\nfrom vim_turing_machine.vim_constants import VIM_MOVE_TAPE_BACKWARDS\nfrom vim_turing_machine.vim_constants import VIM_MOVE_TAPE_FORWARDS\nfrom vim_turing_machine.vim_constants import VIM_NEXT_STATE\nfrom vim_turing_machine.vim_constants import VIM_POINTERS\nfrom vim_turing_machine.vim_constants import VIM_RUN_REGISTER\nfrom vim_turing_machine.vim_constants import VIM_TAPE_WRAP_POSITION\nfrom vim_turing_machine.vim_constants import VIM_TEMPLATE\n\n\ndef create_initial_tape(input_tape):\n    \"\"\"Generates the initial tape by padding the input and wrapping\"\"\"\n    padding_length = VIM_TAPE_WRAP_POSITION - len(input_tape) % VIM_TAPE_WRAP_POSITION\n    input_tape += padding_length * [BLANK_CHARACTER]\n\n    initial_tape = []\n    for index, value in enumerate(input_tape):\n        if index % VIM_TAPE_WRAP_POSITION == 0:\n            initial_tape.append([])\n        initial_tape[index // VIM_TAPE_WRAP_POSITION].append(value)\n\n    return '\\n'.join(' '.join(row) for row in initial_tape)\n\n\nclass VimStateTransitionAdapter(object):\n\n    def __init__(self, state_transition):\n        self.st = state_transition\n\n    def to_vim(self):\n        \"\"\"Returns vim command mapping of this transition\"\"\"\n        return (\n            '_{}-{}:{}{}{}{}'\n        ).format(\n            self.st.previous_state,\n            self.st.previous_character,\n            self._change_state_to(),\n            self._change_tape_to(),\n            self._move_pointer(),\n            VIM_NEXT_STATE,\n        )\n\n    def _change_state_to(self):\n        \"\"\"Returns the vim commands to change current state to next\"\"\"\n        return '`k\"_C{}\u001b'.format(self.st.next_state)\n\n    def _change_tape_to(self):\n        \"\"\"Returns the vim commands to change current tape value to next\"\"\"\n        return '`t\"_cw{}\u001b'.format(self.st.next_character)\n\n    def _move_pointer(self):\n        \"\"\"Returns the vim commands to move the tape after transition\"\"\"\n        if self.st.tape_pointer_direction == FORWARDS:\n            return VIM_MOVE_TAPE_FORWARDS\n        elif self.st.tape_pointer_direction == BACKWARDS:\n            return VIM_MOVE_TAPE_BACKWARDS\n        else:\n            return ''\n\n\nclass VimTuringMachine(TuringMachine):\n\n    def run(self, initial_tape, auto_step=True):\n        \"\"\"Generates vim machine in an output file\"\"\"\n        self.initialize_machine(initial_tape)\n\n        with open(VIM_MACHINE_FILENAME, 'w') as machine:\n            machine.write(VIM_TEMPLATE.format(\n                initial_state=self.current_state,\n                initial_tape=create_initial_tape(self.tape),\n                characters_per_line=VIM_TAPE_WRAP_POSITION,\n                pointers=VIM_POINTERS,\n                blank_character=BLANK_CHARACTER,\n                logging=(\n                    VIM_LOG_TAPE_AND_STATE if auto_step and self._debug else ''\n                ),\n                state_transitions='\\n'.join(\n                    VimStateTransitionAdapter(state_transition).to_vim()\n                    for state_transition in self._state_transitions\n                ),\n            ).replace(VIM_RUN_REGISTER, VIM_RUN_REGISTER if auto_step else ''))\n\n        print('Machine written to {}'.format(VIM_MACHINE_FILENAME))\n"
  },
  {
    "path": "vimrc",
    "content": "set whichwrap+=b,s\nset cursorline\nset cursorcolumn\n"
  }
]