├── CNAME ├── .gitignore ├── requirements-dev.txt ├── python ├── pydot-1.4.1-py2.py3-none-any.whl ├── rcviz_test.py └── rcviz.py ├── package.json ├── .github ├── workflows │ ├── javascript.yaml │ └── python.yaml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── pull_request_template.md └── code_of_conduct.md ├── pyproject.toml ├── README.md ├── old ├── hanoi.html ├── vis.css ├── count_stair_ways4.json ├── find_zero_binary.json ├── tree.html ├── index.html ├── fib5.json ├── simple_tree.json ├── fib4.json ├── make_gasket1.json ├── count_k.json ├── tree.js ├── num_partitions63.json ├── vis.js └── make_gasket2.json ├── css └── recursive_tree_viz.css ├── javascript └── recursive_tree_viz.js ├── index.html ├── eslint.config.mjs ├── rendered ├── sprout_leaves1.html ├── sprout_leaves2.html ├── fact3.html ├── fib5.html └── virfib5.html └── LICENSE /CNAME: -------------------------------------------------------------------------------- 1 | www.recursionvisualizer.com -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | __pycache__ 3 | .coverage 4 | venv/ 5 | .venv/ 6 | node_modules 7 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | pydot 2 | pytest==7.1.2 3 | black==22.3.0 4 | flake8==4.0.1 5 | coverage==6.4.1 6 | pytest-cov==3.0.0 7 | -------------------------------------------------------------------------------- /python/pydot-1.4.1-py2.py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pamelafox/recursive-visualizations/HEAD/python/pydot-1.4.1-py2.py3-none-any.whl -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "eslint": "^9.8.0", 4 | "@eslint/eslintrc": "^3.1.0", 5 | "@eslint/js": "^9.8.0", 6 | "globals": "^15.9.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.github/workflows/javascript.yaml: -------------------------------------------------------------------------------- 1 | name: Javascript checks 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - name: Install ESLint 12 | run: npm install 13 | - name: Run ESLint 14 | run: npx eslint javascript/ -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 100 3 | target-version = ['py39'] 4 | 5 | [tool.pytest.ini_options] 6 | addopts = "-ra --cov" 7 | testpaths = [ 8 | "python" 9 | ] 10 | pythonpath = ['python'] 11 | 12 | [tool.coverage.paths] 13 | source = ["python"] 14 | 15 | [tool.coverage.report] 16 | show_missing = true 17 | fail_under = 100 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # recursive-visualizations 2 | 3 | A way to visualize the call graph of recursive functions. 4 | 5 | Uses Pyodide to run rcviz.py and then a WASM PyDot/GraphViz port to build an SVG graph. 6 | Finally, some JavaScript adds a slider for stepping through the calls. 7 | 8 | To run locally: 9 | 10 | ```python3 -m http.server``` 11 | 12 | To run Python tests: 13 | 14 | ```pytest``` 15 | -------------------------------------------------------------------------------- /old/hanoi.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Towers of Hanoi 5 | 6 | 7 | 8 |

You can move a disk by clicking it, dragging it over to the desired peg, and releasing it.

9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /old/vis.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, sans-serif; 3 | } 4 | 5 | .node { 6 | background: white; 7 | border: 2px solid #ccc; 8 | color: #757575; 9 | border-radius: 120px; 10 | position: absolute; 11 | left: 0; 12 | width: 50px; 13 | height: 25px; 14 | text-align: center; 15 | font-family: monospace; 16 | padding: 10px 10px 0px 10px; 17 | z-index: 1; 18 | } 19 | 20 | 21 | .node-activated { 22 | color: #12bf96; 23 | border: 2px solid #12bf96; 24 | } 25 | 26 | .jtk-connector { 27 | z-index: 0; 28 | 29 | } 30 | 31 | .jtk-connector-activated { 32 | z-index: 0; 33 | color: turquoise; 34 | } 35 | 36 | input[type=range] { 37 | vertical-align:middle; 38 | } 39 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### High-level description of change 2 | 3 | 4 | ### Have you added tests to cover this new/updated code? 5 | 6 | _REPLACE THIS: Remember to check the code coverage report for 100% coverage of your new code, or note if it's not needed_ 7 | 8 | ### Risks involved 9 | 10 | _REPLACE THIS: Note anything that could go wrong. Good opportunity to think through edge cases_ 11 | 12 | ### Are there any dependencies or blockers for merging this? 13 | 14 | _REPLACE THIS: Note any branches that must be merged elsewhere, e.g._ 15 | 16 | ### How can we immediately test that it works? 17 | 18 | _REPLACE THIS: Describe how we can check this change works from end to end, like test function code and a screenshot of expected output._ 19 | -------------------------------------------------------------------------------- /old/count_stair_ways4.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "count_stair_ways(4)", 3 | "id": "0x7fdf3548a240->0x7fdf35061440", 4 | "children": [ 5 | { 6 | "label": "count_stair_ways(3)", 7 | "id": "0x7fdf3530d3d0->0x7fdf3548a240->0x7fdf35061440", 8 | "children": [ 9 | { 10 | "label": "count_stair_ways(2)", 11 | "id": "0x7fdf335cddc0->0x7fdf3530d3d0->0x7fdf3548a240->0x7fdf35061440", 12 | "children": [] 13 | }, 14 | { 15 | "label": "count_stair_ways(1)", 16 | "id": "0x7fdf335cddc0->0x7fdf3530d3d0->0x7fdf3548a240->0x7fdf35061440", 17 | "children": [] 18 | } 19 | ] 20 | }, 21 | { 22 | "label": "count_stair_ways(2)", 23 | "id": "0x7fdf335cddc0->0x7fdf3548a240->0x7fdf35061440", 24 | "children": [] 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /css/recursive_tree_viz.css: -------------------------------------------------------------------------------- 1 | .graph .node, .graph .edge, .graph .node text { 2 | visibility: hidden; 3 | } 4 | .graph .node.activated text:first-of-type { 5 | visibility: visible; 6 | } 7 | .graph .node.activated path { 8 | visibility: visible; 9 | } 10 | .graph .node text.activated { 11 | visibility: visible; 12 | } 13 | .graph .edge.activated { 14 | visibility: visible; 15 | } 16 | .svg-controls { 17 | width: 600px; 18 | display: grid; 19 | grid-template-rows: 1fr; 20 | grid-template-columns: 100px 1fr 100px; 21 | grid-gap: 8px; 22 | background: #80808070; 23 | } 24 | .svg-controls button:first-of-type { 25 | grid-row: 1; 26 | grid-column: 1; 27 | } 28 | .svg-controls input { 29 | grid-row: 1; 30 | grid-column: 2; 31 | } 32 | .svg-controls button:last-of-type { 33 | grid-row: 1; 34 | grid-column: 3; 35 | } -------------------------------------------------------------------------------- /old/find_zero_binary.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "find_zero(1, 20, at 0x7fb20154bee0>)", 3 | "id": "0x7fb20148a240->0x7fb1fff61440", 4 | "children": [ 5 | { 6 | "label": "find_zero(1, 9, at 0x7fb20154bee0>)", 7 | "id": "0x7fb201515e40->0x7fb20148a240->0x7fb1fff61440", 8 | "children": [ 9 | { 10 | "label": "find_zero(1, 4, at 0x7fb20154bee0>)", 11 | "id": "0x7fb201556200->0x7fb201515e40->0x7fb20148a240->0x7fb1fff61440", 12 | "children": [ 13 | { 14 | "label": "find_zero(3, 4, at 0x7fb20154bee0>)", 15 | "id": "0x7fb201556040->0x7fb201556200->0x7fb201515e40->0x7fb20148a240->0x7fb1fff61440", 16 | "children": [] 17 | } 18 | ] 19 | } 20 | ] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /old/tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Tree visualization 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /old/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Recursive tree visualization 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.github/workflows/python.yaml: -------------------------------------------------------------------------------- 1 | name: Python checks 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - name: Set up Python 3 12 | uses: actions/setup-python@v3 13 | with: 14 | python-version: 3.9 15 | - name: Install dependencies 16 | run: | 17 | python -m pip install --upgrade pip 18 | pip install -r requirements-dev.txt 19 | - name: Lint with flake8 20 | run: | 21 | # stop the build if there are Python syntax errors or undefined names 22 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 23 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 24 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 25 | - name: Check formatting with black 26 | uses: psf/black@stable 27 | with: 28 | src: "python/" 29 | options: "--check --verbose" 30 | - name: Run unit tests 31 | run: | 32 | pytest 33 | -------------------------------------------------------------------------------- /old/fib5.json: -------------------------------------------------------------------------------- 1 | { 2 | "label":"fib(5)", 3 | "children":[ 4 | { 5 | "label":"fib(4)", 6 | "children":[ 7 | { 8 | "label":"fib(3)", 9 | "children":[ 10 | { 11 | "label":"fib(2)", 12 | "children":[ 13 | { 14 | "label":"fib(1)" 15 | }, 16 | { 17 | "label":"fib(0)" 18 | } 19 | ] 20 | }, 21 | { 22 | "label":"fib(1)" 23 | } 24 | ] 25 | }, 26 | { 27 | "label":"fib(2)", 28 | "children":[ 29 | { 30 | "label":"fib(1)" 31 | }, 32 | { 33 | "label":"fib(0)" 34 | } 35 | ] 36 | } 37 | ] 38 | }, 39 | { 40 | "label":"fib(3)", 41 | "children":[ 42 | { 43 | "label":"fib(2)", 44 | "children":[ 45 | { 46 | "label":"fib(1)" 47 | }, 48 | { 49 | "label":"fib(0)" 50 | } 51 | ] 52 | }, 53 | { 54 | "label":"fib(1)" 55 | } 56 | ] 57 | } 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /old/simple_tree.json: -------------------------------------------------------------------------------- 1 | { 2 | "label":"20", 3 | "id":"0x7ff37c8579f0->0x7ff37c861440", 4 | "children":[ 5 | { 6 | "label":"12", 7 | "id":"0x7ff37c960c10->0x7ff37c8579f0->0x7ff37c861440", 8 | "children":[ 9 | { 10 | "label":"9", 11 | "id":"0x7ff37c9c31f0->0x7ff37c960c10->0x7ff37c8579f0->0x7ff37c861440", 12 | "children":[ 13 | { 14 | "label":"7", 15 | "id":"0x7ff37c976200->0x7ff37c9c31f0->0x7ff37c960c10->0x7ff37c8579f0->0x7ff37c861440", 16 | "children":[ 17 | 18 | ] 19 | }, 20 | { 21 | "label":"2", 22 | "id":"0x7ff37c976200->0x7ff37c9c31f0->0x7ff37c960c10->0x7ff37c8579f0->0x7ff37c861440", 23 | "children":[ 24 | 25 | ] 26 | } 27 | ] 28 | }, 29 | { 30 | "label":"3", 31 | "id":"0x7ff37c976200->0x7ff37c960c10->0x7ff37c8579f0->0x7ff37c861440", 32 | "children":[ 33 | 34 | ] 35 | } 36 | ] 37 | }, 38 | { 39 | "label":"8", 40 | "id":"0x7ff37c976200->0x7ff37c8579f0->0x7ff37c861440", 41 | "children":[ 42 | { 43 | "label":"4", 44 | "id":"0x7ff37c960c10->0x7ff37c976200->0x7ff37c8579f0->0x7ff37c861440", 45 | "children":[ 46 | 47 | ] 48 | }, 49 | { 50 | "label":"4", 51 | "id":"0x7ff37c960c10->0x7ff37c976200->0x7ff37c8579f0->0x7ff37c861440", 52 | "children":[ 53 | 54 | ] 55 | } 56 | ] 57 | } 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /old/fib4.json: -------------------------------------------------------------------------------- 1 | { 2 | "label":"fib(4)", 3 | "id":"0x7ff37c8579f0->0x7ff37c861440", 4 | "children":[ 5 | { 6 | "label":"fib(3)", 7 | "id":"0x7ff37c960c10->0x7ff37c8579f0->0x7ff37c861440", 8 | "children":[ 9 | { 10 | "label":"fib(2)", 11 | "id":"0x7ff37c9c31f0->0x7ff37c960c10->0x7ff37c8579f0->0x7ff37c861440", 12 | "children":[ 13 | { 14 | "label":"fib(1)", 15 | "id":"0x7ff37c976200->0x7ff37c9c31f0->0x7ff37c960c10->0x7ff37c8579f0->0x7ff37c861440", 16 | "children":[ 17 | 18 | ] 19 | }, 20 | { 21 | "label":"fib(0)", 22 | "id":"0x7ff37c976200->0x7ff37c9c31f0->0x7ff37c960c10->0x7ff37c8579f0->0x7ff37c861440", 23 | "children":[ 24 | 25 | ] 26 | } 27 | ] 28 | }, 29 | { 30 | "label":"fib(1)", 31 | "id":"0x7ff37c976200->0x7ff37c960c10->0x7ff37c8579f0->0x7ff37c861440", 32 | "children":[ 33 | 34 | ] 35 | } 36 | ] 37 | }, 38 | { 39 | "label":"fib(2)", 40 | "id":"0x7ff37c976200->0x7ff37c8579f0->0x7ff37c861440", 41 | "children":[ 42 | { 43 | "label":"fib(1)", 44 | "id":"0x7ff37c960c10->0x7ff37c976200->0x7ff37c8579f0->0x7ff37c861440", 45 | "children":[ 46 | 47 | ] 48 | }, 49 | { 50 | "label":"fib(0)", 51 | "id":"0x7ff37c960c10->0x7ff37c976200->0x7ff37c8579f0->0x7ff37c861440", 52 | "children":[ 53 | 54 | ] 55 | } 56 | ] 57 | } 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /old/make_gasket1.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "mg(1, 0, 0, 300)", 3 | "id": "0x7fc56f68a240->0x7fc56f459440", 4 | "children": [ 5 | { 6 | "label": "mg(0, 0, 0, 150)", 7 | "id": "0x7fc56f00efb0->0x7fc56f68a240->0x7fc56f459440", 8 | "children": [ 9 | { 10 | "label": "t(0, 0, 150)", 11 | "id": "0x7fc56f73ba00->0x7fc56f00efb0->0x7fc56f68a240->0x7fc56f459440", 12 | "children": [] 13 | }, 14 | { 15 | "label": "t(150, 0, 150)", 16 | "id": "0x7fc56f73ba00->0x7fc56f00efb0->0x7fc56f68a240->0x7fc56f459440", 17 | "children": [] 18 | }, 19 | { 20 | "label": "t(75, 130, 150)", 21 | "id": "0x7fc56f73ba00->0x7fc56f00efb0->0x7fc56f68a240->0x7fc56f459440", 22 | "children": [] 23 | } 24 | ] 25 | }, 26 | { 27 | "label": "mg(0, 150, 0, 150)", 28 | "id": "0x7fc56f00efb0->0x7fc56f68a240->0x7fc56f459440", 29 | "children": [ 30 | { 31 | "label": "t(150, 0, 150)", 32 | "id": "0x7fc56f73ba00->0x7fc56f00efb0->0x7fc56f68a240->0x7fc56f459440", 33 | "children": [] 34 | }, 35 | { 36 | "label": "t(75, 130, 150)", 37 | "id": "0x7fc56f73ba00->0x7fc56f00efb0->0x7fc56f68a240->0x7fc56f459440", 38 | "children": [] 39 | } 40 | ] 41 | }, 42 | { 43 | "label": "mg(0, 75, 130, 150)", 44 | "id": "0x7fc56f00efb0->0x7fc56f68a240->0x7fc56f459440", 45 | "children": [ 46 | { 47 | "label": "t(75, 130, 150)", 48 | "id": "0x7fc56f73ba00->0x7fc56f00efb0->0x7fc56f68a240->0x7fc56f459440", 49 | "children": [] 50 | } 51 | ] 52 | } 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /old/count_k.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "count_k(3, 3)", 3 | "id": "0x7f9827c8a240->0x7f9825f61440", 4 | "children": [ 5 | { 6 | "label": "count_k(2, 3)", 7 | "id": "0x7f9825c1db30->0x7f9827c8a240->0x7f9825f61440", 8 | "children": [ 9 | { 10 | "label": "count_k(1, 3)", 11 | "id": "0x7f9827f3ce40->0x7f9825c1db30->0x7f9827c8a240->0x7f9825f61440", 12 | "children": [ 13 | { 14 | "label": "count_k(0, 3)", 15 | "id": "0x7f9827f3cc80->0x7f9827f3ce40->0x7f9825c1db30->0x7f9827c8a240->0x7f9825f61440", 16 | "children": [] 17 | }, 18 | { 19 | "label": "count_k(-1, 3)", 20 | "id": "0x7f9827f3cc80->0x7f9827f3ce40->0x7f9825c1db30->0x7f9827c8a240->0x7f9825f61440", 21 | "children": [] 22 | }, 23 | { 24 | "label": "count_k(-2, 3)", 25 | "id": "0x7f9827f3cc80->0x7f9827f3ce40->0x7f9825c1db30->0x7f9827c8a240->0x7f9825f61440", 26 | "children": [] 27 | } 28 | ] 29 | }, 30 | { 31 | "label": "count_k(0, 3)", 32 | "id": "0x7f9827f3cc80->0x7f9825c1db30->0x7f9827c8a240->0x7f9825f61440", 33 | "children": [] 34 | }, 35 | { 36 | "label": "count_k(-1, 3)", 37 | "id": "0x7f9827f3cc80->0x7f9825c1db30->0x7f9827c8a240->0x7f9825f61440", 38 | "children": [] 39 | } 40 | ] 41 | }, 42 | { 43 | "label": "count_k(1, 3)", 44 | "id": "0x7f9827f3cc80->0x7f9827c8a240->0x7f9825f61440", 45 | "children": [ 46 | { 47 | "label": "count_k(0, 3)", 48 | "id": "0x7f9825c1db30->0x7f9827f3cc80->0x7f9827c8a240->0x7f9825f61440", 49 | "children": [] 50 | }, 51 | { 52 | "label": "count_k(-1, 3)", 53 | "id": "0x7f9825c1db30->0x7f9827f3cc80->0x7f9827c8a240->0x7f9825f61440", 54 | "children": [] 55 | }, 56 | { 57 | "label": "count_k(-2, 3)", 58 | "id": "0x7f9825c1db30->0x7f9827f3cc80->0x7f9827c8a240->0x7f9825f61440", 59 | "children": [] 60 | } 61 | ] 62 | }, 63 | { 64 | "label": "count_k(0, 3)", 65 | "id": "0x7f9825c1db30->0x7f9827c8a240->0x7f9825f61440", 66 | "children": [] 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /old/tree.js: -------------------------------------------------------------------------------- 1 | 2 | class TreeViz { 3 | 4 | constructor(div, tree, options) { 5 | this.div = div; 6 | this.tree = tree; 7 | options = options || {}; 8 | this.width = options.width; 9 | this.nodeWidth = 74; 10 | this.plumber = jsPlumb.getInstance(); 11 | this.maxTop = 0; 12 | this.nodes = []; 13 | } 14 | 15 | draw() { 16 | this.drawNode(this.tree, 0, this.width/2); 17 | } 18 | 19 | drawNode(nodeInfo, depth, x, parentDiv) { 20 | var div = document.createElement("div"); 21 | let label = nodeInfo.label; 22 | div.innerText = label; 23 | div.className = "node node-activated"; 24 | var y = depth * 70; 25 | div.style.top = y + "px"; 26 | this.maxTop = Math.max(y, this.maxTop); 27 | div.style.left = (x - this.nodeWidth/2) + "px"; 28 | div.addEventListener("click", this.highlightNode.bind(this)); 29 | div.addEventListener("mouseover", this.highlightNode.bind(this)); 30 | this.div.appendChild(div); 31 | this.nodes.push(div); 32 | 33 | var descriptor = "Inner node"; 34 | if (this.nodes.length == 1) { 35 | descriptor = "The root node of entire tree"; 36 | } else if (nodeInfo.children.length) { 37 | descriptor += " and root node of subtree"; 38 | } else { 39 | descriptor = "A leaf node"; 40 | } 41 | 42 | tippy(div, { 43 | allowHTML: true, 44 | content: descriptor + '
Label: ' + label, 45 | trigger: 'mouseenter click' 46 | }); 47 | 48 | nodeInfo.div = div; 49 | nodeInfo.parentDiv = parentDiv;; 50 | 51 | if (parentDiv) { 52 | this.plumber.setContainer(this.div); 53 | this.plumber.connect({ 54 | source: parentDiv, target: div, 55 | anchor: "Center", endpoint: "Blank", 56 | connector: ["Straight"], 57 | paintStyle:{ stroke:"#12bf96", strokeWidth:2 } 58 | }); 59 | } 60 | if (nodeInfo.children) { 61 | var childDepth = depth + 1; 62 | var maxChildrenAtDepth = Math.pow(nodeInfo.children.length, childDepth); 63 | var spaceBetween = (this.width - (this.nodeWidth * maxChildrenAtDepth)) / ( maxChildrenAtDepth + 1); 64 | var theseChildrenSpace = (this.nodeWidth * nodeInfo.children.length) + (spaceBetween * (nodeInfo.children.length + 1)); 65 | var childrenStartX = x - (spaceBetween/2 + this.nodeWidth/2); 66 | if (nodeInfo.children.length === 3) { 67 | childrenStartX = x - (spaceBetween + this.nodeWidth); 68 | } 69 | nodeInfo.children.forEach((child, i) => 70 | this.drawNode(child, childDepth, (childrenStartX + i * (spaceBetween + this.nodeWidth)), div) 71 | ); 72 | } 73 | } 74 | 75 | highlightNode(event) { 76 | const node = event.target; 77 | this.nodes.forEach((node, _) => { 78 | node.style.zIndex = 1; 79 | }); 80 | node.style.zIndex = 300; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /python/rcviz_test.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import pydot 3 | 4 | import rcviz 5 | 6 | 7 | virfib_def = """ 8 | def virfib(n): 9 | if n == 0: 10 | return 0 11 | if n == 1: 12 | return 1 13 | else: 14 | return virfib(n - 1) + virfib(n - 2)""" 15 | 16 | 17 | def test_visualize(): 18 | dotgraph_str = rcviz.visualize(virfib_def, "virfib(3)") 19 | dotgraph = pydot.graph_from_dot_data(dotgraph_str)[0] 20 | 21 | assert len(dotgraph.get_nodes()) == 6 22 | assert len(dotgraph.get_edges()) == 9 23 | assert [n.get_label() for n in dotgraph.get_nodes()] == [ 24 | '"virfib(3)"', 25 | '"virfib(2)"', 26 | '"virfib(1)"', 27 | '"virfib(0)"', 28 | '"virfib(1)"', 29 | "Result", 30 | ] 31 | assert [e.get_label() for e in dotgraph.get_edges()] == [ 32 | '"(#1)"', 33 | '"1 (#6)"', 34 | '"(#7)"', 35 | '"1 (#8)"', 36 | '"(#2)"', 37 | '"1 (#3)"', 38 | '"(#4)"', 39 | '"0 (#5)"', 40 | '"2 (#9)"', 41 | ] 42 | 43 | 44 | def test_error_too_many_nodes(monkeypatch): 45 | monkeypatch.setattr(rcviz, "MAX_FRAMES", 10) 46 | 47 | with pytest.raises(rcviz.TooManyFramesError): 48 | rcviz.visualize(virfib_def, "virfib(14)") 49 | 50 | 51 | def test_error_too_much_time(monkeypatch): 52 | while_def = """ 53 | def inf(): 54 | while True: 55 | inf() 56 | """ 57 | monkeypatch.setattr(rcviz, "MAX_TIME", 2) 58 | 59 | with pytest.raises(rcviz.TooMuchTimeError): 60 | rcviz.visualize(while_def, "inf()") 61 | 62 | 63 | def test_mutable_args(): 64 | rev_def = """ 65 | def rev(lst, start, end): 66 | if start < end: 67 | tmp = lst[end] 68 | lst[end] = lst[start] 69 | lst[start] = tmp 70 | rev(lst, start+1, end-1) 71 | """ 72 | dotgraph_str = rcviz.visualize(rev_def, "rev([1, 2, 3, 4, 5], 0, 4)") 73 | dotgraph = pydot.graph_from_dot_data(dotgraph_str)[0] 74 | 75 | assert len(dotgraph.get_nodes()) == 3 76 | assert len(dotgraph.get_edges()) == 2 77 | assert [n.get_label() for n in dotgraph.get_nodes()] == [ 78 | '"rev([1, 2, 3, 4, 5], 0, 4)"', 79 | '"rev([5, 2, 3, 4, 1], 1, 3)"', 80 | '"rev([5, 4, 3, 2, 1], 2, 2)"', 81 | ] 82 | assert [e.get_label() for e in dotgraph.get_edges()] == ['"(#1)"', '"(#2)"'] 83 | 84 | 85 | def test_dict_args(): 86 | # Ensure that curly braces get escaped, since curly braces have meaning in dot graphs. 87 | 88 | dict_def = """ 89 | def finditem(obj, key): 90 | if key in obj: 91 | return obj[key] 92 | for k, v in obj.items(): 93 | if isinstance(v, dict): 94 | item = finditem(v, key) 95 | if item is not None: 96 | return item 97 | """ 98 | 99 | dotgraph_str = rcviz.visualize(dict_def, "finditem({'B':1,'A': {'C': 2}},'C')") 100 | dotgraph = pydot.graph_from_dot_data(dotgraph_str)[0] 101 | 102 | assert [n.get_label() for n in dotgraph.get_nodes()] == [ 103 | "\"finditem(\\{'B': 1, 'A': \\{'C': 2\\}\\}, C)\"", 104 | "\"finditem(\\{'C': 2\\}, C)\"", 105 | "Result", 106 | ] 107 | -------------------------------------------------------------------------------- /old/num_partitions63.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "num_partitions(6, 3)", 3 | "id": "0x7f83c6c8a240->0x7f83c6a61440", 4 | "children": [ 5 | { 6 | "label": "num_partitions(3, 3)", 7 | "id": "0x7f83c6918c30->0x7f83c6c8a240->0x7f83c6a61440", 8 | "children": [ 9 | { 10 | "label": "num_partitions(0, 3)", 11 | "id": "0x7f83c6f3de40->0x7f83c6918c30->0x7f83c6c8a240->0x7f83c6a61440", 12 | "children": [ 13 | { 14 | "label": "num_partitions(-3, 3)", 15 | "id": "0x7f83c6f3dc80->0x7f83c6f3de40->0x7f83c6918c30->0x7f83c6c8a240->0x7f83c6a61440", 16 | "children": [ 17 | { 18 | "label": "num_partitions(-2, 2)", 19 | "id": "0x7f83c6f713c0->0x7f83c6f3dc80->0x7f83c6f3de40->0x7f83c6918c30->0x7f83c6c8a240->0x7f83c6a61440", 20 | "children": [] 21 | }, 22 | { 23 | "label": "num_partitions(0, 1)", 24 | "id": "0x7f83c6f713c0->0x7f83c6f3dc80->0x7f83c6f3de40->0x7f83c6918c30->0x7f83c6c8a240->0x7f83c6a61440", 25 | "children": [] 26 | } 27 | ] 28 | }, 29 | { 30 | "label": "num_partitions(0, 2)", 31 | "id": "0x7f83c6f3dc80->0x7f83c6f3de40->0x7f83c6918c30->0x7f83c6c8a240->0x7f83c6a61440", 32 | "children": [ 33 | { 34 | "label": "num_partitions(-2, 2)", 35 | "id": "0x7f83c6f713c0->0x7f83c6f3dc80->0x7f83c6f3de40->0x7f83c6918c30->0x7f83c6c8a240->0x7f83c6a61440", 36 | "children": [] 37 | }, 38 | { 39 | "label": "num_partitions(0, 1)", 40 | "id": "0x7f83c6f713c0->0x7f83c6f3dc80->0x7f83c6f3de40->0x7f83c6918c30->0x7f83c6c8a240->0x7f83c6a61440", 41 | "children": [] 42 | } 43 | ] 44 | } 45 | ] 46 | }, 47 | { 48 | "label": "num_partitions(3, 2)", 49 | "id": "0x7f83c6f713c0->0x7f83c6918c30->0x7f83c6c8a240->0x7f83c6a61440", 50 | "children": [ 51 | { 52 | "label": "num_partitions(1, 2)", 53 | "id": "0x7f83c6f71580->0x7f83c6f713c0->0x7f83c6918c30->0x7f83c6c8a240->0x7f83c6a61440", 54 | "children": [ 55 | { 56 | "label": "num_partitions(-1, 2)", 57 | "id": "0x7f83c6f3de40->0x7f83c6f71580->0x7f83c6f713c0->0x7f83c6918c30->0x7f83c6c8a240->0x7f83c6a61440", 58 | "children": [] 59 | }, 60 | { 61 | "label": "num_partitions(1, 1)", 62 | "id": "0x7f83c6f3de40->0x7f83c6f71580->0x7f83c6f713c0->0x7f83c6918c30->0x7f83c6c8a240->0x7f83c6a61440", 63 | "children": [] 64 | } 65 | ] 66 | }, 67 | { 68 | "label": "num_partitions(3, 1)", 69 | "id": "0x7f83c6f3de40->0x7f83c6f713c0->0x7f83c6918c30->0x7f83c6c8a240->0x7f83c6a61440", 70 | "children": [] 71 | } 72 | ] 73 | } 74 | ] 75 | }, 76 | { 77 | "label": "num_partitions(6, 2)", 78 | "id": "0x7f83c6f3de40->0x7f83c6c8a240->0x7f83c6a61440", 79 | "children": [ 80 | { 81 | "label": "num_partitions(4, 2)", 82 | "id": "0x7f83c6918c30->0x7f83c6f3de40->0x7f83c6c8a240->0x7f83c6a61440", 83 | "children": [ 84 | { 85 | "label": "num_partitions(2, 2)", 86 | "id": "0x7f83c6f71200->0x7f83c6918c30->0x7f83c6f3de40->0x7f83c6c8a240->0x7f83c6a61440", 87 | "children": [ 88 | { 89 | "label": "num_partitions(0, 2)", 90 | "id": "0x7f83c6f71740->0x7f83c6f71200->0x7f83c6918c30->0x7f83c6f3de40->0x7f83c6c8a240->0x7f83c6a61440", 91 | "children": [ 92 | { 93 | "label": "num_partitions(-2, 2)", 94 | "id": "0x7f83c6f713c0->0x7f83c6f71740->0x7f83c6f71200->0x7f83c6918c30->0x7f83c6f3de40->0x7f83c6c8a240->0x7f83c6a61440", 95 | "children": [] 96 | }, 97 | { 98 | "label": "num_partitions(0, 1)", 99 | "id": "0x7f83c6f713c0->0x7f83c6f71740->0x7f83c6f71200->0x7f83c6918c30->0x7f83c6f3de40->0x7f83c6c8a240->0x7f83c6a61440", 100 | "children": [] 101 | } 102 | ] 103 | }, 104 | { 105 | "label": "num_partitions(2, 1)", 106 | "id": "0x7f83c6f713c0->0x7f83c6f71200->0x7f83c6918c30->0x7f83c6f3de40->0x7f83c6c8a240->0x7f83c6a61440", 107 | "children": [] 108 | } 109 | ] 110 | }, 111 | { 112 | "label": "num_partitions(4, 1)", 113 | "id": "0x7f83c6f713c0->0x7f83c6918c30->0x7f83c6f3de40->0x7f83c6c8a240->0x7f83c6a61440", 114 | "children": [] 115 | } 116 | ] 117 | }, 118 | { 119 | "label": "num_partitions(6, 1)", 120 | "id": "0x7f83c6f713c0->0x7f83c6f3de40->0x7f83c6c8a240->0x7f83c6a61440", 121 | "children": [] 122 | } 123 | ] 124 | } 125 | ] 126 | } 127 | -------------------------------------------------------------------------------- /javascript/recursive_tree_viz.js: -------------------------------------------------------------------------------- 1 | export class RecursiveTreeViz { 2 | 3 | constructor(svg, options) { 4 | this.svg = svg; 5 | options = options || {}; 6 | this.startStep = options.startStep; 7 | this.steps = []; 8 | this.frames = {}; 9 | } 10 | 11 | draw() { 12 | // Store all the frames 13 | const frames = this.svg.querySelectorAll(".node"); 14 | for (var i = 0; i < frames.length; i++) { 15 | const frame = frames[i]; 16 | const frameId = frame.getElementsByTagName("title")[0].textContent; 17 | this.frames[frameId] = frame; 18 | } 19 | 20 | // Determine number of steps 21 | const edges = this.svg.querySelectorAll(".edge"); 22 | for (let i = 0; i < edges.length; i++) { 23 | const edge = edges[i]; 24 | const edgeTitle = edge.getElementsByTagName("title")[0].textContent; 25 | // Check for recently introduced bug in Graphviz where some edges have no text 26 | if (edge.getElementsByTagName("text").length > 0) { 27 | const edgeText = edge.getElementsByTagName("text")[0].textContent; 28 | let stepNum, isReturn; 29 | stepNum = parseInt(edgeText.split("(#")[1], 10); 30 | if (edgeTitle.endsWith(":c")) { 31 | isReturn = true; 32 | edge.getElementsByTagName("text")[0].textContent = edgeText.split("(#")[0]; 33 | } else { 34 | edge.getElementsByTagName("text")[0].textContent = ""; 35 | isReturn = false; 36 | } 37 | const edgeFrames = edgeTitle.split(":c")[0]; 38 | const parentFrameId = edgeFrames.split("->")[0]; 39 | const childFrameId = edgeFrames.split("->")[1]; 40 | this.steps[stepNum] = { 41 | parentFrame: this.frames[parentFrameId], 42 | childFrame: this.frames[childFrameId], 43 | edge: edge, 44 | isReturn: isReturn 45 | }; 46 | } 47 | } 48 | 49 | if (this.startStep == null || isNaN(this.startStep)) { 50 | this.currentStep = this.steps.length - 1; 51 | } else { 52 | this.currentStep = this.startStep; 53 | } 54 | this.drawControls(); 55 | this.toggleSteps(); 56 | } 57 | 58 | 59 | drawControls() { 60 | var controlsDiv = document.createElement("div"); 61 | controlsDiv.classList.add("svg-controls"); 62 | 63 | this.prevButton = document.createElement("button"); 64 | this.prevButton.innerText = "< Prev"; 65 | if (this.currentStep <= 0) { 66 | this.prevButton.setAttribute("disabled", "disabled"); 67 | } 68 | this.prevButton.addEventListener("click", this.onPrevClick.bind(this)); 69 | document.addEventListener("keypress", (e) => { 70 | if (e.key == "p" || e.key == "ArrowLeft") { 71 | this.onPrevClick(); 72 | } else if (e.key == "n" || e.key == "ArrowRight") { 73 | this.onNextClick(); 74 | } 75 | }); 76 | 77 | this.nextButton = document.createElement("button"); 78 | this.nextButton.innerText = "> Next"; 79 | if (this.currentStep >= this.steps.length - 1) { 80 | this.nextButton.setAttribute("disabled", "disabled"); 81 | } 82 | this.nextButton.addEventListener("click", this.onNextClick.bind(this)); 83 | 84 | this.slider = document.createElement("input"); 85 | this.slider.setAttribute("type", "range"); 86 | this.slider.setAttribute("min", 0); 87 | this.slider.setAttribute("max", this.steps.length - 1); 88 | this.slider.setAttribute("value", this.currentStep); 89 | this.slider.addEventListener("change", this.onSliderChange.bind(this)); 90 | 91 | controlsDiv.appendChild(this.prevButton); 92 | controlsDiv.appendChild(this.slider); 93 | controlsDiv.appendChild(this.nextButton); 94 | 95 | this.svg.before(controlsDiv); 96 | } 97 | 98 | toggleSteps() { 99 | this.steps.forEach((step, stepI) => { 100 | if (stepI == 1) { 101 | step.parentFrame && step.parentFrame.classList.add("activated"); 102 | } 103 | if (stepI <= this.currentStep) { 104 | step.edge.classList.add("activated"); 105 | step.childFrame.classList.add("activated"); 106 | } else { 107 | step.edge.classList.remove("activated"); 108 | !step.isReturn && step.childFrame.classList.remove("activated"); 109 | } 110 | }) 111 | } 112 | 113 | onSliderChange(event) { 114 | this.currentStep = parseInt(event.target.value, 10); 115 | this.fixButtons(); 116 | this.toggleSteps(); 117 | } 118 | 119 | onPrevClick() { 120 | this.currentStep--; 121 | this.fixButtons(); 122 | this.slider.value = this.currentStep; 123 | this.toggleSteps(); 124 | } 125 | 126 | onNextClick() { 127 | this.currentStep++; 128 | this.fixButtons(); 129 | this.slider.value = this.currentStep; 130 | this.toggleSteps(); 131 | } 132 | 133 | fixButtons() { 134 | if (this.currentStep === 0) { 135 | this.prevButton.setAttribute("disabled", "disabled"); 136 | } 137 | if (this.currentStep < this.steps.length - 1) { 138 | this.nextButton.removeAttribute("disabled"); 139 | } 140 | if (this.currentStep > 0) { 141 | this.prevButton.removeAttribute("disabled"); 142 | } 143 | if (this.currentStep === this.steps.length - 1) { 144 | this.nextButton.setAttribute("disabled", "disabled"); 145 | } 146 | } 147 | } -------------------------------------------------------------------------------- /old/vis.js: -------------------------------------------------------------------------------- 1 | 2 | class RecursiveTreeViz { 3 | 4 | constructor(div, tree, options) { 5 | this.div = div; 6 | this.tree = tree; 7 | options = options || {}; 8 | this.width = options.width; 9 | this.nodeWidth = 74; 10 | this.startStep = options.startStep; 11 | this.funcName = options.funcName; 12 | this.plumber = jsPlumb.getInstance(); 13 | this.maxTop = 0; 14 | this.steps = []; 15 | } 16 | 17 | draw() { 18 | this.drawNode(this.tree, 0, this.width/2); 19 | if (this.startStep == null || isNaN(this.startStep)) { 20 | this.currentStep = this.steps.length - 1; 21 | } else { 22 | this.currentStep = this.startStep; 23 | } 24 | this.drawControls(); 25 | this.toggleSteps(); 26 | } 27 | 28 | drawNode(nodeInfo, depth, x, parentDiv) { 29 | var div = document.createElement("div"); 30 | let label = nodeInfo.label; 31 | if (this.funcName) { 32 | label = this.funcName + "(" + label.split("(")[1] 33 | } 34 | label = label.replace(", ", ","); 35 | div.innerText = label; 36 | div.className = "node"; 37 | var y = depth * 70; 38 | div.style.top = y + "px"; 39 | this.maxTop = Math.max(y, this.maxTop); 40 | div.style.left = (x - this.nodeWidth/2) + "px"; 41 | div.addEventListener("click", this.highlightNode.bind(this)); 42 | div.addEventListener("mouseover", this.highlightNode.bind(this)); 43 | this.div.appendChild(div); 44 | 45 | nodeInfo.div = div; 46 | nodeInfo.parentDiv = parentDiv;; 47 | this.steps.push(nodeInfo); 48 | 49 | if (parentDiv) { 50 | this.plumber.setContainer(this.div); 51 | this.plumber.connect({ 52 | source: parentDiv, target: div, 53 | anchor: "Center", endpoint: "Blank", 54 | connector: ["Straight"], 55 | paintStyle:{ stroke:"#ccc", strokeWidth:2 } 56 | }); 57 | } 58 | if (nodeInfo.children) { 59 | var childDepth = depth + 1; 60 | var maxChildrenAtDepth = Math.pow(nodeInfo.children.length, childDepth); 61 | var spaceBetween = (this.width - (this.nodeWidth * maxChildrenAtDepth)) / ( maxChildrenAtDepth + 1); 62 | var theseChildrenSpace = (this.nodeWidth * nodeInfo.children.length) + (spaceBetween * (nodeInfo.children.length + 1)); 63 | var childrenStartX = x - (spaceBetween/2 + this.nodeWidth/2); 64 | if (nodeInfo.children.length === 3) { 65 | childrenStartX = x - (spaceBetween + this.nodeWidth); 66 | } 67 | nodeInfo.children.forEach((child, i) => 68 | this.drawNode(child, childDepth, (childrenStartX + i * (spaceBetween + this.nodeWidth)), div) 69 | ); 70 | } 71 | } 72 | 73 | drawControls() { 74 | var controlsDiv = document.createElement("div"); 75 | controlsDiv.style.position = "absolute"; 76 | controlsDiv.style.left = (this.width/2 - 125) + "px"; 77 | controlsDiv.style.top = this.maxTop + 50 + "px"; 78 | 79 | this.prevButton = document.createElement("button"); 80 | this.prevButton.innerText = "< Prev"; 81 | if (this.currentStep <= 0) { 82 | this.prevButton.setAttribute("disabled", "disabled"); 83 | } 84 | this.prevButton.addEventListener("click", this.onPrevClick.bind(this)); 85 | 86 | this.nextButton = document.createElement("button"); 87 | this.nextButton.innerText = "> Next"; 88 | if (this.currentStep >= this.steps.length - 1) { 89 | this.nextButton.setAttribute("disabled", "disabled"); 90 | } 91 | this.nextButton.addEventListener("click", this.onNextClick.bind(this)); 92 | 93 | this.slider = document.createElement("input"); 94 | this.slider.setAttribute("type", "range"); 95 | this.slider.setAttribute("min", 0); 96 | this.slider.setAttribute("max", this.steps.length - 1); 97 | this.slider.setAttribute("value", this.currentStep); 98 | this.slider.addEventListener("change", this.onSliderChange.bind(this)); 99 | 100 | controlsDiv.appendChild(this.prevButton); 101 | controlsDiv.appendChild(this.slider); 102 | controlsDiv.appendChild(this.nextButton); 103 | this.div.appendChild(controlsDiv); 104 | } 105 | 106 | highlightNode(event) { 107 | const node = event.target; 108 | this.steps.forEach((step, stepI) => { 109 | step.div.style.zIndex = 1; 110 | }); 111 | node.style.zIndex = 300; 112 | } 113 | 114 | toggleSteps() { 115 | 116 | let prevDiv; 117 | this.steps.forEach((step, stepI) => { 118 | var connectors = this.plumber.getConnections( 119 | {source: step.parentDiv, target: step.div}); 120 | if (stepI <= this.currentStep) { 121 | step.div.className = "node node-activated"; 122 | connectors.forEach((connection) => { 123 | connection.setPaintStyle({ stroke:"#12bf96", strokeWidth:2 }) 124 | }); 125 | } else { 126 | step.div.className = "node"; 127 | connectors.forEach((connection) => { 128 | connection.setPaintStyle({ stroke:"#ccc", strokeWidth:2 }) 129 | }); 130 | } 131 | prevDiv = step.div; 132 | }) 133 | } 134 | 135 | onSliderChange(event) { 136 | this.currentStep = parseInt(event.target.value, 10); 137 | this.toggleSteps(); 138 | } 139 | 140 | onPrevClick(event) { 141 | this.currentStep--; 142 | if (this.currentStep === 0) { 143 | this.prevButton.setAttribute("disabled", "disabled"); 144 | } 145 | if (this.currentStep < this.steps.length - 1) { 146 | this.nextButton.removeAttribute("disabled"); 147 | } 148 | this.slider.value = this.currentStep; 149 | this.toggleSteps(); 150 | } 151 | 152 | onNextClick(event) { 153 | this.currentStep++; 154 | if (this.currentStep > 0) { 155 | this.prevButton.removeAttribute("disabled"); 156 | } 157 | if (this.currentStep === this.steps.length - 1) { 158 | this.nextButton.setAttribute("disabled", "disabled"); 159 | } 160 | this.slider.value = this.currentStep; 161 | this.toggleSteps(); 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /.github/code_of_conduct.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, caste, color, religion, or sexual 10 | identity and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or advances of 31 | any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email address, 35 | without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at kjaymiller@gmail.com. 63 | All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series of 85 | actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or permanent 92 | ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within the 112 | community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.1, available at 118 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 119 | 120 | Community Impact Guidelines were inspired by 121 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 122 | 123 | For answers to common questions about this code of conduct, see the FAQ at 124 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 125 | [https://www.contributor-covenant.org/translations][translations]. 126 | 127 | [homepage]: https://www.contributor-covenant.org 128 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 129 | [Mozilla CoC]: https://github.com/mozilla/diversity 130 | [FAQ]: https://www.contributor-covenant.org/faq 131 | [translations]: https://www.contributor-covenant.org/translations 132 | -------------------------------------------------------------------------------- /python/rcviz.py: -------------------------------------------------------------------------------- 1 | # rcviz : a small recursion call graph vizualization decorator 2 | # Copyright (c) Ran Dugal 2014 3 | # Licensed under the GPLv2, which is available at 4 | # http://www.gnu.org/licenses/gpl-2.0.html 5 | from time import time 6 | import inspect 7 | import copy 8 | from typing import Dict, List, Any 9 | 10 | import pydot 11 | 12 | MAX_FRAMES = 1000 13 | MAX_TIME = 10 14 | 15 | 16 | class TooManyFramesError(Exception): 17 | pass 18 | 19 | 20 | class TooMuchTimeError(Exception): 21 | pass 22 | 23 | 24 | class callgraph(object): 25 | """singleton class that stores global graph data 26 | draw graph using pygraphviz 27 | """ 28 | 29 | _callers: Dict[int, Any] = {} # caller_fn_id : node_data 30 | _counter = 1 # track call order 31 | _unwindcounter = 1 # track unwind order 32 | _step = 1 # track overall steps 33 | _frames: List[int] = [] # keep frame objects reference 34 | 35 | @staticmethod 36 | def reset(): 37 | callgraph._callers = {} 38 | callgraph._counter = 1 39 | callgraph._frames = [] 40 | callgraph._unwindcounter = 1 41 | callgraph._step = 1 42 | 43 | @staticmethod 44 | def get_callers(): 45 | return callgraph._callers 46 | 47 | @staticmethod 48 | def increment(): 49 | callgraph._counter += 1 50 | callgraph._step += 1 51 | 52 | @staticmethod 53 | def increment_unwind(): 54 | callgraph._unwindcounter += 1 55 | callgraph._step += 1 56 | 57 | @staticmethod 58 | def get_frames(): 59 | return callgraph._frames 60 | 61 | @staticmethod 62 | def render(): 63 | dotgraph = pydot.Dot("rc-graph", graph_type="digraph", strict=False) 64 | 65 | # Create nodes 66 | for frame_id, node in callgraph.get_callers().items(): 67 | label = f"{node.fn_name}({node.argstr()})" 68 | dotgraph.add_node(pydot.Node(frame_id, label=label, shape="Mrecord")) 69 | 70 | # Create edges 71 | for frame_id, node in callgraph.get_callers().items(): 72 | child_nodes = [] 73 | for child_id, counter in node.child_methods: 74 | child_nodes.append(child_id) 75 | label = f"(#{counter})" 76 | dotgraph.add_edge(pydot.Edge(frame_id, child_id, color="black", label=label)) 77 | 78 | # Order edges left to right 79 | if len(child_nodes) > 1: 80 | subgraph = pydot.Subgraph(rank="same") 81 | prev_node = None 82 | for child_node in child_nodes: 83 | subgraph.add_node(pydot.Node(child_node)) 84 | if prev_node: 85 | subgraph.add_edge(pydot.Edge(prev_node, child_node)) 86 | prev_node = child_node 87 | dotgraph.add_subgraph(subgraph) 88 | 89 | parent_frame = None 90 | for frame_id, node in callgraph.get_callers().items(): 91 | for child_id, counter in node.child_methods: 92 | child_node = callgraph.get_callers().get(child_id) 93 | if child_node and child_node.ret is not None: 94 | ret_label = f"{child_node.ret} (#{child_node.ret_step})" 95 | dotgraph.add_edge( 96 | pydot.Edge( 97 | frame_id, 98 | child_id, 99 | dir="back", 100 | label=ret_label, 101 | color="green", 102 | headport="c", 103 | ) 104 | ) 105 | if parent_frame is None: 106 | parent_frame = frame_id 107 | if node.ret is not None: 108 | ret_label = f"{node.ret} (#{node.ret_step})" 109 | dotgraph.add_node(pydot.Node(99999999, shape="Mrecord", label="Result")) 110 | dotgraph.add_edge( 111 | pydot.Edge( 112 | 99999999, 113 | frame_id, 114 | dir="back", 115 | label=ret_label, 116 | color="Green", 117 | headport="c", 118 | ) 119 | ) 120 | 121 | return dotgraph.to_string() 122 | 123 | 124 | class node_data(object): 125 | def __init__(self, _args=None, _kwargs=None, _fn_name=""): 126 | self.args = _args 127 | self.kwargs = _kwargs 128 | self.fn_name = _fn_name 129 | self.ret = None 130 | self.child_methods = [] 131 | 132 | def argstr(self): 133 | s_args = ", ".join([str(arg) for arg in self.args]) 134 | s_kwargs = ", ".join([(str(k), str(v)) for (k, v) in self.kwargs.items()]) 135 | return f"{s_args}{s_kwargs}".replace("{", "\{").replace("}", "\}") 136 | 137 | 138 | class viz(object): 139 | """decorator to construct the call graph with args and return values as labels""" 140 | 141 | def __init__(self, wrapped): 142 | self.wrapped = wrapped 143 | self.max_frames = MAX_FRAMES 144 | self.max_time = MAX_TIME # in seconds 145 | self.start_time = time() 146 | 147 | def __call__(self, *args, **kwargs): 148 | 149 | g_callers = callgraph.get_callers() 150 | g_frames = callgraph.get_frames() 151 | 152 | # find the caller frame, and add self as a child node 153 | caller_frame_id = None 154 | 155 | fullstack = inspect.stack() 156 | 157 | this_frame_id = id(fullstack[0][0]) 158 | if fullstack[2].function == "__call__": 159 | caller_frame_id = id(fullstack[2][0]) 160 | 161 | if this_frame_id not in g_frames: 162 | g_frames.append(fullstack[0][0]) 163 | 164 | if this_frame_id not in g_callers.keys(): 165 | g_callers[this_frame_id] = node_data( 166 | copy.deepcopy(args), copy.deepcopy(kwargs), self.wrapped.__name__ 167 | ) 168 | 169 | edgeinfo = None 170 | if caller_frame_id: 171 | edgeinfo = [this_frame_id, callgraph._step] 172 | g_callers[caller_frame_id].child_methods.append(edgeinfo) 173 | callgraph.increment() 174 | 175 | if len(g_frames) > self.max_frames: 176 | raise TooManyFramesError( 177 | f"Encountered more than ${self.max_frames} while executing function" 178 | ) 179 | if (time() - self.start_time) > self.max_time: 180 | raise TooMuchTimeError(f"Took more than ${self.max_time} seconds to run function") 181 | 182 | # Invoke the wrapped 183 | ret = self.wrapped(*args, **kwargs) 184 | 185 | g_callers[this_frame_id].ret_step = callgraph._step 186 | 187 | if edgeinfo: 188 | callgraph.increment_unwind() 189 | 190 | g_callers[this_frame_id].ret = copy.deepcopy(ret) 191 | 192 | return ret 193 | 194 | 195 | def decorate_funcs(func_source: str): 196 | outlines = [] 197 | for line in func_source.split("\n"): 198 | if line.startswith("def "): 199 | outlines.append("@viz") 200 | outlines.append(line) 201 | return "\n".join(outlines) 202 | 203 | 204 | def visualize(function_definition, function_call): 205 | """Either returns generated SVG or generates an error.""" 206 | callgraph.reset() 207 | function_definition = decorate_funcs(function_definition) 208 | exec(function_definition, globals()) 209 | eval(function_call) 210 | return callgraph.render() 211 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Recursion Visualizer 8 | 9 | 10 | 11 | 17 | 20 | 21 | 22 |
23 | 24 |

Visualize a recursive function

25 |
26 | 34 | 38 |
39 | 43 |
44 |
45 | 46 |
47 | 48 | Loading libraries... 49 | 58 | 59 | 72 | 73 | 99 | 100 |
101 | 102 |
103 | 104 | 109 | 110 |
111 |

👋🏻 Are you comfortable publicly sharing your visualizations? I'd love to see how folks are using this tool. 112 | Post a link in the discussions 113 | or @ me on social media (Twitter, Mastodon) 114 |

115 |

Source code on Github. Thank you @carlsborg for the rcviz library.

116 |
117 | 118 |
119 | 120 | 121 | 122 | 123 | 124 | 146 | 147 | 188 | 189 | 190 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import path from "node:path"; 3 | import { fileURLToPath } from "node:url"; 4 | import js from "@eslint/js"; 5 | import { FlatCompat } from "@eslint/eslintrc"; 6 | 7 | const __filename = fileURLToPath(import.meta.url); 8 | const __dirname = path.dirname(__filename); 9 | const compat = new FlatCompat({ 10 | baseDirectory: __dirname, 11 | recommendedConfig: js.configs.recommended, 12 | allConfig: js.configs.all 13 | }); 14 | 15 | export default [...compat.extends("eslint:recommended"), { 16 | languageOptions: { 17 | globals: { 18 | ...globals.browser, 19 | }, 20 | 21 | ecmaVersion: 2015, 22 | sourceType: "module", 23 | }, 24 | 25 | rules: { 26 | "accessor-pairs": "error", 27 | "array-bracket-newline": "error", 28 | "array-bracket-spacing": "error", 29 | "array-callback-return": "error", 30 | "array-element-newline": "error", 31 | "arrow-body-style": "error", 32 | "arrow-parens": ["error", "always"], 33 | 34 | "arrow-spacing": ["error", { 35 | after: true, 36 | before: true, 37 | }], 38 | 39 | "block-scoped-var": "error", 40 | "block-spacing": "error", 41 | "brace-style": ["error", "1tbs"], 42 | "callback-return": "error", 43 | camelcase: "error", 44 | "capitalized-comments": ["error", "always"], 45 | "class-methods-use-this": "error", 46 | "comma-dangle": "error", 47 | 48 | "comma-spacing": ["error", { 49 | after: true, 50 | before: false, 51 | }], 52 | 53 | "comma-style": ["error", "last"], 54 | complexity: "error", 55 | "computed-property-spacing": ["error", "never"], 56 | "consistent-return": "error", 57 | "consistent-this": "error", 58 | curly: "error", 59 | "default-case": "error", 60 | "dot-location": "error", 61 | "dot-notation": "error", 62 | "eol-last": ["error", "never"], 63 | eqeqeq: "off", 64 | "for-direction": "error", 65 | "func-call-spacing": "error", 66 | "func-name-matching": "error", 67 | "func-names": "error", 68 | "func-style": "error", 69 | "function-paren-newline": "error", 70 | "generator-star-spacing": "error", 71 | "getter-return": "error", 72 | "global-require": "error", 73 | "guard-for-in": "error", 74 | "handle-callback-err": "error", 75 | "id-blacklist": "error", 76 | "id-length": "off", 77 | "id-match": "error", 78 | "implicit-arrow-linebreak": ["error", "beside"], 79 | indent: "off", 80 | "indent-legacy": "off", 81 | "init-declarations": "off", 82 | "jsx-quotes": "error", 83 | "key-spacing": "error", 84 | 85 | "keyword-spacing": ["error", { 86 | after: true, 87 | before: true, 88 | }], 89 | 90 | "line-comment-position": "error", 91 | "linebreak-style": ["error", "unix"], 92 | "lines-around-comment": "error", 93 | "lines-around-directive": "error", 94 | "lines-between-class-members": ["error", "always"], 95 | "max-depth": "error", 96 | "max-len": "off", 97 | "max-lines": "error", 98 | "max-nested-callbacks": "error", 99 | "max-params": "error", 100 | "max-statements": "off", 101 | "max-statements-per-line": "error", 102 | "multiline-comment-style": "error", 103 | "multiline-ternary": "error", 104 | "new-cap": "error", 105 | "new-parens": "error", 106 | "newline-after-var": ["error", "never"], 107 | "newline-before-return": "error", 108 | "newline-per-chained-call": "error", 109 | "no-alert": "error", 110 | "no-array-constructor": "error", 111 | "no-await-in-loop": "error", 112 | "no-bitwise": "error", 113 | "no-buffer-constructor": "error", 114 | "no-caller": "error", 115 | "no-catch-shadow": "error", 116 | "no-confusing-arrow": "error", 117 | "no-continue": "error", 118 | "no-div-regex": "error", 119 | "no-duplicate-imports": "error", 120 | "no-else-return": "error", 121 | "no-empty-function": "error", 122 | "no-eq-null": "off", 123 | "no-eval": "error", 124 | "no-extend-native": "error", 125 | "no-extra-bind": "error", 126 | "no-extra-label": "error", 127 | "no-extra-parens": "error", 128 | "no-floating-decimal": "error", 129 | "no-implicit-coercion": "error", 130 | "no-implicit-globals": "error", 131 | "no-implied-eval": "error", 132 | "no-inline-comments": "error", 133 | "no-inner-declarations": ["error", "functions"], 134 | "no-invalid-this": "error", 135 | "no-iterator": "error", 136 | "no-label-var": "error", 137 | "no-labels": "error", 138 | "no-lone-blocks": "error", 139 | "no-lonely-if": "error", 140 | "no-loop-func": "error", 141 | "no-magic-numbers": "off", 142 | "no-mixed-operators": "error", 143 | "no-mixed-requires": "error", 144 | "no-multi-assign": "error", 145 | "no-multi-spaces": "error", 146 | "no-multi-str": "error", 147 | "no-multiple-empty-lines": "error", 148 | "no-native-reassign": "error", 149 | "no-negated-condition": "error", 150 | "no-negated-in-lhs": "error", 151 | "no-nested-ternary": "error", 152 | "no-new": "error", 153 | "no-new-func": "error", 154 | "no-new-object": "error", 155 | "no-new-require": "error", 156 | "no-new-wrappers": "error", 157 | "no-octal-escape": "error", 158 | "no-param-reassign": "off", 159 | "no-path-concat": "error", 160 | "no-plusplus": "off", 161 | "no-process-env": "error", 162 | "no-process-exit": "error", 163 | "no-proto": "error", 164 | "no-prototype-builtins": "error", 165 | "no-restricted-globals": "error", 166 | "no-restricted-imports": "error", 167 | "no-restricted-modules": "error", 168 | "no-restricted-properties": "error", 169 | "no-restricted-syntax": "error", 170 | "no-return-assign": "error", 171 | "no-return-await": "error", 172 | "no-script-url": "error", 173 | "no-self-compare": "error", 174 | "no-sequences": "error", 175 | "no-shadow": "off", 176 | "no-shadow-restricted-names": "error", 177 | "no-spaced-func": "error", 178 | "no-sync": "error", 179 | "no-tabs": "error", 180 | "no-template-curly-in-string": "error", 181 | "no-ternary": "error", 182 | "no-throw-literal": "error", 183 | "no-trailing-spaces": "error", 184 | "no-undef-init": "error", 185 | "no-undefined": "error", 186 | "no-underscore-dangle": "error", 187 | "no-unmodified-loop-condition": "error", 188 | "no-unneeded-ternary": "error", 189 | "no-use-before-define": "error", 190 | "no-useless-call": "error", 191 | "no-useless-computed-key": "error", 192 | "no-useless-concat": "error", 193 | "no-useless-constructor": "error", 194 | "no-useless-rename": "error", 195 | "no-useless-return": "error", 196 | "no-var": "off", 197 | "no-void": "error", 198 | "no-warning-comments": "error", 199 | "no-whitespace-before-property": "error", 200 | "no-with": "error", 201 | "nonblock-statement-body-position": "error", 202 | "object-curly-newline": "error", 203 | "object-curly-spacing": "error", 204 | "object-property-newline": "error", 205 | "object-shorthand": "off", 206 | "one-var": "off", 207 | "one-var-declaration-per-line": ["error", "initializations"], 208 | "operator-assignment": "error", 209 | "operator-linebreak": "error", 210 | "padded-blocks": "off", 211 | "padding-line-between-statements": "error", 212 | "prefer-arrow-callback": "error", 213 | "prefer-const": "off", 214 | "prefer-destructuring": "off", 215 | "prefer-numeric-literals": "error", 216 | "prefer-promise-reject-errors": "error", 217 | "prefer-reflect": "error", 218 | "prefer-rest-params": "error", 219 | "prefer-spread": "error", 220 | "prefer-template": "error", 221 | "quote-props": "off", 222 | quotes: ["error", "double"], 223 | radix: ["error", "always"], 224 | "require-await": "error", 225 | "rest-spread-spacing": "error", 226 | semi: "off", 227 | 228 | "semi-spacing": ["error", { 229 | after: true, 230 | before: false, 231 | }], 232 | 233 | "semi-style": ["error", "last"], 234 | "sort-imports": "error", 235 | "sort-keys": "off", 236 | "sort-vars": "off", 237 | "space-before-blocks": "error", 238 | "space-before-function-paren": "off", 239 | "space-in-parens": ["error", "never"], 240 | "space-infix-ops": "error", 241 | "space-unary-ops": "error", 242 | "spaced-comment": ["error", "always"], 243 | strict: "error", 244 | "switch-colon-spacing": "error", 245 | "symbol-description": "error", 246 | "template-curly-spacing": "error", 247 | "template-tag-spacing": "error", 248 | "unicode-bom": ["error", "never"], 249 | "vars-on-top": "off", 250 | "wrap-iife": "error", 251 | "wrap-regex": "error", 252 | "yield-star-spacing": "error", 253 | yoda: ["error", "never"], 254 | }, 255 | }]; -------------------------------------------------------------------------------- /rendered/sprout_leaves1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Recursion Visualizer 8 | 9 | 10 | 50 | 51 | 52 | 53 | 55 | 57 | 58 | 60 | 61 | 62 | 63 | 64 | 140252381745632 65 | 66 | sprout_leaves([2], [4, 5]) 67 | 68 | 69 | 70 | 140251306129104 71 | 72 | sprout_leaves([3], [4, 5]) 73 | 74 | 75 | 76 | 140251842498304 77 | 78 | sprout_leaves([1, [2], [3]], [4, 5]) 79 | 80 | 81 | 82 | 140251842498304->140252381745632 83 | 84 | 85 | (#1) 86 | 87 | 88 | 89 | 140251842498304->140252381745632:c 90 | 91 | 92 | [2, [4], [5]] (#2) 93 | 94 | 95 | 96 | 140251842498304->140251306129104 97 | 98 | 99 | (#3) 100 | 101 | 102 | 103 | 140251842498304->140251306129104:c 104 | 105 | 106 | [3, [4], [5]] (#4) 107 | 108 | 109 | 110 | 99999999 111 | 112 | Result 113 | 114 | 115 | 116 | 99999999->140251842498304:c 117 | 118 | 119 | [1, [2, [4], [5]], [3, [4], [5]]] (#5) 120 | 121 | 122 | 123 | 124 | 125 | 126 | 271 | 272 | 273 | -------------------------------------------------------------------------------- /rendered/sprout_leaves2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Recursion Visualizer 9 | 10 | 11 | 51 | 52 | 53 | 54 | 56 | 58 | 59 | 61 | 62 | 63 | 64 | 65 | 140251306112112 66 | 67 | sprout_leaves([1, [2, [3]]], [6, 1, 2]) 68 | 69 | 70 | 71 | 140251842495488 72 | 73 | sprout_leaves([2, [3]], [6, 1, 2]) 74 | 75 | 76 | 77 | 140251306112112->140251842495488 78 | 79 | 80 | (#1) 81 | 82 | 83 | 84 | 140251306112112->140251842495488:c 85 | 86 | 87 | [2, [3, [6], [1], [2]]] (#4) 88 | 89 | 90 | 91 | 140251306113168 92 | 93 | sprout_leaves([3], [6, 1, 2]) 94 | 95 | 96 | 97 | 140251842495488->140251306113168 98 | 99 | 100 | (#2) 101 | 102 | 103 | 104 | 140251842495488->140251306113168:c 105 | 106 | 107 | [3, [6], [1], [2]] (#3) 108 | 109 | 110 | 111 | 99999999 112 | 113 | Result 114 | 115 | 116 | 117 | 99999999->140251306112112:c 118 | 119 | 120 | [1, [2, [3, [6], [1], [2]]]] (#5) 121 | 122 | 123 | 124 | 125 | 126 | 127 | 272 | 273 | 274 | -------------------------------------------------------------------------------- /old/make_gasket2.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "mg(2, 0, 0, 300)", 3 | "id": "0x7f888ca8a240->0x7f888b761440", 4 | "children": [ 5 | { 6 | "label": "mg(1, 0, 0, 150.0)", 7 | "id": "0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 8 | "children": [ 9 | { 10 | "label": "mg(0, 0, 0, 75.0)", 11 | "id": "0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 12 | "children": [ 13 | { 14 | "label": "t(0, 0, 75.0)", 15 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 16 | "children": [] 17 | }, 18 | { 19 | "label": "t(75.0, 0, 75.0)", 20 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 21 | "children": [] 22 | }, 23 | { 24 | "label": "t(37.5, 64.9519052838329, 75.0)", 25 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 26 | "children": [] 27 | }, 28 | { 29 | "label": "t(75.0, 129.9038105676658, 75.0)", 30 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 31 | "children": [] 32 | }, 33 | { 34 | "label": "t(150.0, 129.9038105676658, 75.0)", 35 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 36 | "children": [] 37 | }, 38 | { 39 | "label": "t(112.5, 194.8557158514987, 75.0)", 40 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 41 | "children": [] 42 | } 43 | ] 44 | }, 45 | { 46 | "label": "mg(0, 75.0, 0, 75.0)", 47 | "id": "0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 48 | "children": [ 49 | { 50 | "label": "t(75.0, 0, 75.0)", 51 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 52 | "children": [] 53 | }, 54 | { 55 | "label": "t(37.5, 64.9519052838329, 75.0)", 56 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 57 | "children": [] 58 | }, 59 | { 60 | "label": "t(75.0, 129.9038105676658, 75.0)", 61 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 62 | "children": [] 63 | }, 64 | { 65 | "label": "t(150.0, 129.9038105676658, 75.0)", 66 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 67 | "children": [] 68 | }, 69 | { 70 | "label": "t(112.5, 194.8557158514987, 75.0)", 71 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 72 | "children": [] 73 | } 74 | ] 75 | }, 76 | { 77 | "label": "mg(0, 37.5, 64.9519052838329, 75.0)", 78 | "id": "0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 79 | "children": [ 80 | { 81 | "label": "t(37.5, 64.9519052838329, 75.0)", 82 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 83 | "children": [] 84 | }, 85 | { 86 | "label": "t(75.0, 129.9038105676658, 75.0)", 87 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 88 | "children": [] 89 | }, 90 | { 91 | "label": "t(150.0, 129.9038105676658, 75.0)", 92 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 93 | "children": [] 94 | }, 95 | { 96 | "label": "t(112.5, 194.8557158514987, 75.0)", 97 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 98 | "children": [] 99 | } 100 | ] 101 | }, 102 | { 103 | "label": "mg(0, 75.0, 129.9038105676658, 75.0)", 104 | "id": "0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 105 | "children": [ 106 | { 107 | "label": "t(75.0, 129.9038105676658, 75.0)", 108 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 109 | "children": [] 110 | }, 111 | { 112 | "label": "t(75.0, 129.9038105676658, 75.0)", 113 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 114 | "children": [] 115 | }, 116 | { 117 | "label": "t(150.0, 129.9038105676658, 75.0)", 118 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 119 | "children": [] 120 | }, 121 | { 122 | "label": "t(150.0, 129.9038105676658, 75.0)", 123 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 124 | "children": [] 125 | }, 126 | { 127 | "label": "t(112.5, 194.8557158514987, 75.0)", 128 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 129 | "children": [] 130 | }, 131 | { 132 | "label": "t(112.5, 194.8557158514987, 75.0)", 133 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 134 | "children": [] 135 | } 136 | ] 137 | }, 138 | { 139 | "label": "mg(0, 150.0, 129.9038105676658, 75.0)", 140 | "id": "0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 141 | "children": [ 142 | { 143 | "label": "t(150.0, 129.9038105676658, 75.0)", 144 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 145 | "children": [] 146 | }, 147 | { 148 | "label": "t(150.0, 129.9038105676658, 75.0)", 149 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 150 | "children": [] 151 | }, 152 | { 153 | "label": "t(112.5, 194.8557158514987, 75.0)", 154 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 155 | "children": [] 156 | }, 157 | { 158 | "label": "t(112.5, 194.8557158514987, 75.0)", 159 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 160 | "children": [] 161 | } 162 | ] 163 | }, 164 | { 165 | "label": "mg(0, 112.5, 194.8557158514987, 75.0)", 166 | "id": "0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 167 | "children": [ 168 | { 169 | "label": "t(112.5, 194.8557158514987, 75.0)", 170 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 171 | "children": [] 172 | }, 173 | { 174 | "label": "t(112.5, 194.8557158514987, 75.0)", 175 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 176 | "children": [] 177 | } 178 | ] 179 | } 180 | ] 181 | }, 182 | { 183 | "label": "mg(1, 150.0, 0, 150.0)", 184 | "id": "0x7f888cc74040->0x7f888ca8a240->0x7f888b761440", 185 | "children": [ 186 | { 187 | "label": "mg(0, 150.0, 0, 75.0)", 188 | "id": "0x7f888b41f560->0x7f888cc74040->0x7f888ca8a240->0x7f888b761440", 189 | "children": [ 190 | { 191 | "label": "t(150.0, 0, 75.0)", 192 | "id": "0x7f888cc3cc80->0x7f888b41f560->0x7f888cc74040->0x7f888ca8a240->0x7f888b761440", 193 | "children": [] 194 | }, 195 | { 196 | "label": "t(225.0, 0, 75.0)", 197 | "id": "0x7f888cc3cc80->0x7f888b41f560->0x7f888cc74040->0x7f888ca8a240->0x7f888b761440", 198 | "children": [] 199 | }, 200 | { 201 | "label": "t(187.5, 64.9519052838329, 75.0)", 202 | "id": "0x7f888cc3cc80->0x7f888b41f560->0x7f888cc74040->0x7f888ca8a240->0x7f888b761440", 203 | "children": [] 204 | } 205 | ] 206 | }, 207 | { 208 | "label": "mg(0, 225.0, 0, 75.0)", 209 | "id": "0x7f888b41f560->0x7f888cc74040->0x7f888ca8a240->0x7f888b761440", 210 | "children": [ 211 | { 212 | "label": "t(225.0, 0, 75.0)", 213 | "id": "0x7f888cc3cc80->0x7f888b41f560->0x7f888cc74040->0x7f888ca8a240->0x7f888b761440", 214 | "children": [] 215 | }, 216 | { 217 | "label": "t(187.5, 64.9519052838329, 75.0)", 218 | "id": "0x7f888cc3cc80->0x7f888b41f560->0x7f888cc74040->0x7f888ca8a240->0x7f888b761440", 219 | "children": [] 220 | } 221 | ] 222 | }, 223 | { 224 | "label": "mg(0, 187.5, 64.9519052838329, 75.0)", 225 | "id": "0x7f888b41f560->0x7f888cc74040->0x7f888ca8a240->0x7f888b761440", 226 | "children": [ 227 | { 228 | "label": "t(187.5, 64.9519052838329, 75.0)", 229 | "id": "0x7f888cc3cc80->0x7f888b41f560->0x7f888cc74040->0x7f888ca8a240->0x7f888b761440", 230 | "children": [] 231 | } 232 | ] 233 | } 234 | ] 235 | }, 236 | { 237 | "label": "mg(1, 75.0, 129.9038105676658, 150.0)", 238 | "id": "0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 239 | "children": [ 240 | { 241 | "label": "mg(0, 75.0, 129.9038105676658, 75.0)", 242 | "id": "0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 243 | "children": [ 244 | { 245 | "label": "t(75.0, 129.9038105676658, 75.0)", 246 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 247 | "children": [] 248 | }, 249 | { 250 | "label": "t(75.0, 129.9038105676658, 75.0)", 251 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 252 | "children": [] 253 | }, 254 | { 255 | "label": "t(150.0, 129.9038105676658, 75.0)", 256 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 257 | "children": [] 258 | }, 259 | { 260 | "label": "t(150.0, 129.9038105676658, 75.0)", 261 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 262 | "children": [] 263 | }, 264 | { 265 | "label": "t(112.5, 194.8557158514987, 75.0)", 266 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 267 | "children": [] 268 | }, 269 | { 270 | "label": "t(112.5, 194.8557158514987, 75.0)", 271 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 272 | "children": [] 273 | } 274 | ] 275 | }, 276 | { 277 | "label": "mg(0, 150.0, 129.9038105676658, 75.0)", 278 | "id": "0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 279 | "children": [ 280 | { 281 | "label": "t(150.0, 129.9038105676658, 75.0)", 282 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 283 | "children": [] 284 | }, 285 | { 286 | "label": "t(150.0, 129.9038105676658, 75.0)", 287 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 288 | "children": [] 289 | }, 290 | { 291 | "label": "t(112.5, 194.8557158514987, 75.0)", 292 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 293 | "children": [] 294 | }, 295 | { 296 | "label": "t(112.5, 194.8557158514987, 75.0)", 297 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 298 | "children": [] 299 | } 300 | ] 301 | }, 302 | { 303 | "label": "mg(0, 112.5, 194.8557158514987, 75.0)", 304 | "id": "0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 305 | "children": [ 306 | { 307 | "label": "t(112.5, 194.8557158514987, 75.0)", 308 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 309 | "children": [] 310 | }, 311 | { 312 | "label": "t(112.5, 194.8557158514987, 75.0)", 313 | "id": "0x7f888cc3cc80->0x7f888cc74040->0x7f888b41f560->0x7f888ca8a240->0x7f888b761440", 314 | "children": [] 315 | } 316 | ] 317 | } 318 | ] 319 | } 320 | ] 321 | } 322 | -------------------------------------------------------------------------------- /rendered/fact3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Recursion Visualizer 9 | 10 | 11 | 51 | 52 | 53 | 54 | 56 | 58 | 59 | 61 | 62 | 63 | 64 | 65 | 140268176730032 66 | 67 | fact(3) 68 | 69 | 70 | 71 | 140268176708800 72 | 73 | fact(2) 74 | 75 | 76 | 77 | 140268176730032->140268176708800 78 | 79 | 80 | (#1) 81 | 82 | 83 | 84 | 140268176730032->140268176708800:c 85 | 86 | 87 | 2 (#6) 88 | 89 | 90 | 91 | 140268176651776 92 | 93 | fact(1) 94 | 95 | 96 | 97 | 140268176708800->140268176651776 98 | 99 | 100 | (#2) 101 | 102 | 103 | 104 | 140268176708800->140268176651776:c 105 | 106 | 107 | 1 (#5) 108 | 109 | 110 | 111 | 140269251428992 112 | 113 | fact(0) 114 | 115 | 116 | 117 | 140268176651776->140269251428992 118 | 119 | 120 | (#3) 121 | 122 | 123 | 124 | 140268176651776->140269251428992:c 125 | 126 | 127 | 1 (#4) 128 | 129 | 130 | 131 | 99999999 132 | 133 | Result 134 | 135 | 136 | 137 | 99999999->140268176730032:c 138 | 139 | 140 | 6 (#7) 141 | 142 | 143 | 144 | 145 | 146 | 147 | 298 | 299 | 300 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /rendered/fib5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Recursion Visualizer 8 | 9 | 10 | 50 | 51 | 52 | 53 | 55 | 57 | 58 | 60 | 61 | 62 | 63 | 64 | 140268980491808 65 | 66 | fib(4) 67 | 68 | 69 | 70 | 140268176759856 71 | 72 | fib(3) 73 | 74 | 75 | 76 | 140269251436976 77 | 78 | fib(3) 79 | 80 | 81 | 82 | 140268980491808->140269251436976 83 | 84 | 85 | (#2) 86 | 87 | 88 | 89 | 140268980491808->140269251436976:c 90 | 91 | 92 | 2 (#11) 93 | 94 | 95 | 96 | 140269251443360 97 | 98 | fib(2) 99 | 100 | 101 | 102 | 140268980491808->140269251443360 103 | 104 | 105 | (#12) 106 | 107 | 108 | 109 | 140268980491808->140269251443360:c 110 | 111 | 112 | 1 (#17) 113 | 114 | 115 | 116 | 140269249079072 117 | 118 | fib(2) 119 | 120 | 121 | 122 | 140268176759856->140269249079072 123 | 124 | 125 | (#20) 126 | 127 | 128 | 129 | 140268176759856->140269249079072:c 130 | 131 | 132 | 1 (#25) 133 | 134 | 135 | 136 | 140268176766640 137 | 138 | fib(1) 139 | 140 | 141 | 142 | 140268176759856->140268176766640 143 | 144 | 145 | (#26) 146 | 147 | 148 | 149 | 140268176759856->140268176766640:c 150 | 151 | 152 | 1 (#27) 153 | 154 | 155 | 156 | 140268176708800 157 | 158 | fib(2) 159 | 160 | 161 | 162 | 140269251436976->140268176708800 163 | 164 | 165 | (#3) 166 | 167 | 168 | 169 | 140269251436976->140268176708800:c 170 | 171 | 172 | 1 (#8) 173 | 174 | 175 | 176 | 140269251421952 177 | 178 | fib(1) 179 | 180 | 181 | 182 | 140269251436976->140269251421952 183 | 184 | 185 | (#9) 186 | 187 | 188 | 189 | 140269251436976->140269251421952:c 190 | 191 | 192 | 1 (#10) 193 | 194 | 195 | 196 | 140268980505424 197 | 198 | fib(1) 199 | 200 | 201 | 202 | 140269251443360->140268980505424 203 | 204 | 205 | (#13) 206 | 207 | 208 | 209 | 140269251443360->140268980505424:c 210 | 211 | 212 | 1 (#14) 213 | 214 | 215 | 216 | 140269251434896 217 | 218 | fib(0) 219 | 220 | 221 | 222 | 140269251443360->140269251434896 223 | 224 | 225 | (#15) 226 | 227 | 228 | 229 | 140269251443360->140269251434896:c 230 | 231 | 232 | 0 (#16) 233 | 234 | 235 | 236 | 140268176651776 237 | 238 | fib(1) 239 | 240 | 241 | 242 | 140268176708800->140268176651776 243 | 244 | 245 | (#4) 246 | 247 | 248 | 249 | 140268176708800->140268176651776:c 250 | 251 | 252 | 1 (#5) 253 | 254 | 255 | 256 | 140269249078544 257 | 258 | fib(0) 259 | 260 | 261 | 262 | 140268176708800->140269249078544 263 | 264 | 265 | (#6) 266 | 267 | 268 | 269 | 140268176708800->140269249078544:c 270 | 271 | 272 | 0 (#7) 273 | 274 | 275 | 276 | 140269249079600 277 | 278 | fib(1) 279 | 280 | 281 | 282 | 140269249079072->140269249079600 283 | 284 | 285 | (#21) 286 | 287 | 288 | 289 | 140269249079072->140269249079600:c 290 | 291 | 292 | 1 (#22) 293 | 294 | 295 | 296 | 140269249080128 297 | 298 | fib(0) 299 | 300 | 301 | 302 | 140269249079072->140269249080128 303 | 304 | 305 | (#23) 306 | 307 | 308 | 309 | 140269249079072->140269249080128:c 310 | 311 | 312 | 0 (#24) 313 | 314 | 315 | 316 | 140268176767168 317 | 318 | fib(5) 319 | 320 | 321 | 322 | 140268176767168->140268980491808 323 | 324 | 325 | (#1) 326 | 327 | 328 | 329 | 140268176767168->140268980491808:c 330 | 331 | 332 | 3 (#18) 333 | 334 | 335 | 336 | 140268176767168->140268176759856 337 | 338 | 339 | (#19) 340 | 341 | 342 | 343 | 140268176767168->140268176759856:c 344 | 345 | 346 | 2 (#28) 347 | 348 | 349 | 350 | 99999999 351 | 352 | Result 353 | 354 | 355 | 356 | 99999999->140268176767168:c 357 | 358 | 359 | 5 (#29) 360 | 361 | 362 | 363 | 364 | 365 | 366 | 517 | 518 | 519 | -------------------------------------------------------------------------------- /rendered/virfib5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Recursion Visualizer 8 | 9 | 10 | 50 | 51 | 52 | 53 | 55 | 57 | 58 | 60 | 61 | 62 | 63 | 64 | 140269251475920 65 | 66 | virfib(4) 67 | 68 | 69 | 70 | 140268980535344 71 | 72 | virfib(3) 73 | 74 | 75 | 76 | 140269249142688 77 | 78 | virfib(3) 79 | 80 | 81 | 82 | 140269251475920->140269249142688 83 | 84 | 85 | (#2) 86 | 87 | 88 | 89 | 140269251475920->140269249142688:c 90 | 91 | 92 | 2 (#11) 93 | 94 | 95 | 96 | 140268176651776 97 | 98 | virfib(2) 99 | 100 | 101 | 102 | 140269251475920->140268176651776 103 | 104 | 105 | (#12) 106 | 107 | 108 | 109 | 140269251475920->140268176651776:c 110 | 111 | 112 | 1 (#17) 113 | 114 | 115 | 116 | 140269251419344 117 | 118 | virfib(2) 119 | 120 | 121 | 122 | 140268980535344->140269251419344 123 | 124 | 125 | (#20) 126 | 127 | 128 | 129 | 140268980535344->140269251419344:c 130 | 131 | 132 | 1 (#25) 133 | 134 | 135 | 136 | 140268980504848 137 | 138 | virfib(1) 139 | 140 | 141 | 142 | 140268980535344->140268980504848 143 | 144 | 145 | (#26) 146 | 147 | 148 | 149 | 140268980535344->140268980504848:c 150 | 151 | 152 | 1 (#27) 153 | 154 | 155 | 156 | 140269251476448 157 | 158 | virfib(2) 159 | 160 | 161 | 162 | 140269249142688->140269251476448 163 | 164 | 165 | (#3) 166 | 167 | 168 | 169 | 140269249142688->140269251476448:c 170 | 171 | 172 | 1 (#8) 173 | 174 | 175 | 176 | 140268980590160 177 | 178 | virfib(1) 179 | 180 | 181 | 182 | 140269249142688->140268980590160 183 | 184 | 185 | (#9) 186 | 187 | 188 | 189 | 140269249142688->140268980590160:c 190 | 191 | 192 | 1 (#10) 193 | 194 | 195 | 196 | 140268176860608 197 | 198 | virfib(1) 199 | 200 | 201 | 202 | 140268176651776->140268176860608 203 | 204 | 205 | (#13) 206 | 207 | 208 | 209 | 140268176651776->140268176860608:c 210 | 211 | 212 | 1 (#14) 213 | 214 | 215 | 216 | 140269251461696 217 | 218 | virfib(0) 219 | 220 | 221 | 222 | 140268176651776->140269251461696 223 | 224 | 225 | (#15) 226 | 227 | 228 | 229 | 140268176651776->140269251461696:c 230 | 231 | 232 | 0 (#16) 233 | 234 | 235 | 236 | 140268712074336 237 | 238 | virfib(1) 239 | 240 | 241 | 242 | 140269251476448->140268712074336 243 | 244 | 245 | (#4) 246 | 247 | 248 | 249 | 140269251476448->140268712074336:c 250 | 251 | 252 | 1 (#5) 253 | 254 | 255 | 256 | 140269249143216 257 | 258 | virfib(0) 259 | 260 | 261 | 262 | 140269251476448->140269249143216 263 | 264 | 265 | (#6) 266 | 267 | 268 | 269 | 140269251476448->140269249143216:c 270 | 271 | 272 | 0 (#7) 273 | 274 | 275 | 276 | 140268714094112 277 | 278 | virfib(1) 279 | 280 | 281 | 282 | 140269251419344->140268714094112 283 | 284 | 285 | (#21) 286 | 287 | 288 | 289 | 140269251419344->140268714094112:c 290 | 291 | 292 | 1 (#22) 293 | 294 | 295 | 296 | 140268176857392 297 | 298 | virfib(0) 299 | 300 | 301 | 302 | 140269251419344->140268176857392 303 | 304 | 305 | (#23) 306 | 307 | 308 | 309 | 140269251419344->140268176857392:c 310 | 311 | 312 | 0 (#24) 313 | 314 | 315 | 316 | 140269249142160 317 | 318 | virfib(5) 319 | 320 | 321 | 322 | 140269249142160->140269251475920 323 | 324 | 325 | (#1) 326 | 327 | 328 | 329 | 140269249142160->140269251475920:c 330 | 331 | 332 | 3 (#18) 333 | 334 | 335 | 336 | 140269249142160->140268980535344 337 | 338 | 339 | (#19) 340 | 341 | 342 | 343 | 140269249142160->140268980535344:c 344 | 345 | 346 | 2 (#28) 347 | 348 | 349 | 350 | 99999999 351 | 352 | Result 353 | 354 | 355 | 356 | 99999999->140269249142160:c 357 | 358 | 359 | 5 (#29) 360 | 361 | 362 | 363 | 364 | 365 | 366 | 517 | 518 | 519 | --------------------------------------------------------------------------------