{"id":15491784,"url":"https://github.com/tristanpenman/persistent-tree","last_synced_at":"2026-02-07T01:31:20.145Z","repository":{"id":250244854,"uuid":"733705925","full_name":"tristanpenman/persistent-tree","owner":"tristanpenman","description":"An experimental Partially-Persistent Tree implementation for Ruby","archived":false,"fork":false,"pushed_at":"2024-07-27T06:08:04.000Z","size":121,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-10-19T10:23:55.709Z","etag":null,"topics":["partial-persistence","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tristanpenman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-12-20T00:01:17.000Z","updated_at":"2024-07-27T06:08:07.000Z","dependencies_parsed_at":"2024-12-08T06:06:13.439Z","dependency_job_id":"ba320561-b093-44a7-92d3-3ca81761a9f3","html_url":"https://github.com/tristanpenman/persistent-tree","commit_stats":{"total_commits":38,"total_committers":2,"mean_commits":19.0,"dds":0.4473684210526315,"last_synced_commit":"b4911080909ecf07eaff1e4e4f314356f5874d42"},"previous_names":["tristanpenman/persistent-tree"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tristanpenman/persistent-tree","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tristanpenman%2Fpersistent-tree","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tristanpenman%2Fpersistent-tree/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tristanpenman%2Fpersistent-tree/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tristanpenman%2Fpersistent-tree/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tristanpenman","download_url":"https://codeload.github.com/tristanpenman/persistent-tree/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tristanpenman%2Fpersistent-tree/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29183948,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-07T00:44:15.062Z","status":"ssl_error","status_checked_at":"2026-02-07T00:35:01.758Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["partial-persistence","ruby"],"created_at":"2024-10-02T07:56:23.128Z","updated_at":"2026-02-07T01:31:20.129Z","avatar_url":"https://github.com/tristanpenman.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Persistent Tree\n\nThe goal of this gem is to provide a Partially-Persistent Tree implementation for Ruby. A Partially-Persistent Tree can be used to efficiently maintain and access previous versions of tree-based data structures, such as Maps and Sets.\n\nThis work is based on Okasaki's thesis *[Purely Functional Data Structures](http://www.cs.cmu.edu/~rwh/theses/okasaki.pdf)* and lectures available online for the MIT *[Advanced Data Structures](https://courses.csail.mit.edu/6.851/)* course.\n\n## Design\n\nThe `PersistentTree::Map` class provides an interface that is designed to mimic Ruby's built-in Hash class. That is to say that using a `PersistentTree::Map` in place of a Hash should be completely unsurprising.\n\nAs a Partially-Persistent data structure, each update to a Map results in a new version of the Map being spawned using an approach that is memory efficient relative to the number of key-value pairs in the Map. Previous versions of a Map are accessible through read-only 'views' of the data structure.\n\nA `PersistentTree::Map` object provides a method called 'version' that, when called with no arguments, returns the current version. When called with one argument (a version number), it will return a view of the Map at that version.\n\nA user of this library might use it like so:\n\n    m = PersistentTree::Map.new   # Create a new map\n    m.version                     # Returns current version number (0)\n    m['k'] = 'v'                  # Returns stored value ('v')\n    m.version                     # Returns current version number (1)\n    m['k']                        # Returns stored value ('v')\n    m.version(0)                  # Returns a read-only view of version 0\n    m.version(0)['k']             # Returns nil (default value)\n    m.version(0).fetch('k')       # Raises KeyError\n    m.store('k', 'w')             # Returns stored value ('w')\n    m.version(1).fetch('k')       # Returns stored value in version 1 ('v')\n\n## Shortcomings\n\nThis gem is still very much a work-in-progress. Just off the top of my head, any user of this gem should be aware of the following limitations and shortcomings:\n\n* Deletion of key-value pairs is not supported - at least not in a way that will lead to new versions of the map being created\n* Performance is currently unmeasured\n* RSpec tests are provided, but coverage is weak for the underlying Tree class\n* No form of fuzzing has been conducted, so input validation should be considered untested\n\n## Graphviz\n\nThe example script in [bin/example](bin/example) shows how the library can be used to visualise the persistent tree data structure. The script adds the letters 'a' to 'n' to a tree, in random order. After each addition, the tree representing the latest version is dumped to a file in the `tmp` directory. These are written in DOT format, and can be converted to PNG or PDF using `dot`:\n\n    dot -T pdf ./tmp/v14.dot \u003e ./tmp/v14.pdf\n    dot -T png ./tmp/v14.dot \u003e ./tmp/v14.png\n\nAn example is shown here:\n\n![Graphviz Example](example.png)\n\nThere is also a corresponding [example.pdf](./example.pdf).\n\nThe way to interpret this is that the first field in a record is a node's key. After that are its left and right children. And the last field represents a 'mod' applied to that node. If the node has been modified, it shows the version corresponding to the modification, as well as the updated child pointer (either left or right).\n\n### Step-by-step: Two Nodes\n\nHere's how to read the diagram, step-by-step.\n\nWhen we first add a node to the tree (e.g. with value 'B'), it looks like this:\n\n             ┌────(1)────┐\n    Root ───►│ Val = 'B' │\n    (v1)     ├───────────┤\n             │  L = nil  │\n             ├───────────┤\n             │  R = nil  │\n             ├─────┬─────┤\n             │  -  │  /  │\n             └─────┴─────┘\n\nThe root (at version 1) points to the new node, and that's the entire tree. The `L` and `R` pointers for the node are both nil, because it has no children in version 1.\n\nWhen we add the value 'B' to the tree, this is what the tree looks like:\n\n             ┌────(1)────┐\n    Root ───►│ Val = 'B' │\n    (v2)     ├───────────┤\n             │  L = nil  │\n             ├───────────┤\n             │  R = nil  │\n             ├─────┬─────┤    ┌────(2)────┐\n             │  2  │  R  ├───►│ Val = 'C' │\n             └─────┴─────┘    ├───────────┤\n                              │     L     │\n                              ├───────────┤\n                              │     R     │\n                              ├─────┬─────┤\n                              │  -  │  -  │\n                              └─────┴─────┘\n\nThe root still points at the original 'B' node. But now the 'B' node contains a modification. The modification says that, in version 2 of this node, it now has a right child (R), that points to node 'C'.\n\nIf we wanted to query version 1 of the tree, we would still begin at the root. However we would only traverse node 'B', stopping when we see that it contains a modification for version 2.\n\nIf instead we queried version 2 of the tree, we would traverse the modification.\n\n### Step-by-step: New Root\n\nNow we add another node, 'A', which would become the left child of 'B'.\n\nNode 'B' already contains a modification, so we cannot add another one. Instead, we create a _new_ node 'B', containing both pointers. The result is version 3 of the tree:\n\n             ┌────(3)────┐\n    Root ───►│ Val = 'B' │\n    (v3)     ├───────────┤                     ┌────(3)────┐\n             │  L = ...  ├────────────────────►│ Val = 'A' │\n             ├───────────┤    ┌────(2)────┐    ├───────────┤\n             │  R = ...  ├───►│ Val = 'C' │    │  L = nil  │\n             ├─────┬─────┤    ├───────────┤    ├───────────┤\n             │  -  │  /  │    │  L = nil  │    │  R = nil  │\n             └─────┴─────┘    ├───────────┤    ├─────┬─────┤\n                              │  R = nil  │    │  -  │  /  │\n                              ├─────┬─────┤    └─────┴─────┘\n                              │  -  │  /  │\n                              └─────┴─────┘\n\nThe new version of node 'B', created for version 3, no longer contains any modifications. Instead, it has both L and R pointers set from its time of creation.\n\n## License\n\nThis code is licensed under the MIT License.\n\nSee the LICENSE file for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftristanpenman%2Fpersistent-tree","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftristanpenman%2Fpersistent-tree","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftristanpenman%2Fpersistent-tree/lists"}