[
  {
    "path": ".gitignore",
    "content": "/target\n/classes\n/checkouts\npom.xml\npom.xml.asc\n*.jar\n*.class\n/.lein-*\n/.nrepl-port\n.hgignore\n.hg/\nnode_modules/*\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Change Log\nAll notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/).\n\n## [Unreleased]\n### Changed\n- Add a new arity to `make-widget-async` to provide a different widget shape.\n\n## [0.1.1] - 2018-01-05\n### Changed\n- Documentation on how to make the widgets.\n\n### Removed\n- `make-widget-sync` - we're all async, all the time.\n\n### Fixed\n- Fixed widget maker to keep working when daylight savings switches over.\n\n## 0.1.0 - 2018-01-05\n### Added\n- Files from the new template.\n- Widget maker public API - `make-widget-sync`.\n\n[Unreleased]: https://github.com/your-name/schism/compare/0.1.1...HEAD\n[0.1.1]: https://github.com/your-name/schism/compare/0.1.0...0.1.1\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2021 Alex Redington\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "README.md",
    "content": "# schism\n\nA batteries included library of CRDT implementations of Clojure's core\ndata types: Sets, Maps, Vectors, and Lists with support for distributed\nmodification and eventual consistency.\n\n## Dependency Information\n\nLatest release: 0.1.2\n\n[Leiningen](http://github.com/technomancy/leiningen/) and [Boot](http://boot-clj.com)\ndependency information:\n\n```\n[com.holychao/schism \"0.1.2\"]\n```\n\n## Motivation\n\nClojure is one of a handful of languages which can be authored and\nexecuted in both a high performance server environment and in a web\nbrowser. This strength affords many benefits, one of which the\nlanguage does not cover is allowing for concurrent modification of data\nwith rich synchronization semantics.\n\nThere are some other efforts similar to this. Schism's aims are:\n\n- To minimize the locus of concerns outside of collection data\n  structures.\n- To provide collection data structures with inferior performance to\n  Clojure's own persistent data structures, but which only incur\n  sub-linear cost increases, available through the same interfaces.\n- To provide collection data structures with greater storage, and\n  serialization costs than Clojure's own persistent data structures,\n  which grow with an upper bound of the number of elements in the\n  collection, independent of the number of operations against that\n  collection.\n- Allow for a Clojure and ClojureScript processes executing on\n  separate nodes to maintain convergent data.\n\n\n## Limitations\n\nBecause schism refuses to use tombstones, some convergence operations\nwill have different results than structures which will embrace the\ncosts of tombstones.\n\nFor example, Schism's set may drop a recently added element during\nconvergence with certain vector clock states. While this quality is\nundesirable, I accept it more readily than monotonically increasing\nstorage requirements for data explicitly intended for communication\nbetween nodes. Future work may pursue allowing for the convergence\noperation to have some configurability so that vector clocks will\nretain information more eagerly to reduce the incidence of this phenomena.\n\n## Usage\n\n`schism.core` contains functions for generating new collections,\ne.g. `schism.core/convergent-set`. These functions accept arguments\nmuch like their Clojure core equivalents, so `(convergent-set :a :b\n:c)` creates a set equivalent to `#{:a :b :c}`\n\nThese collections support Clojure's collection operations with the\nsame semantic consequences (save for above mentioned performance and\nstorage costs). Any divergence between a schism collection's behavior\nand a Clojure collection's behavior is a bug. Please file a report, as\nthese should be relatively fast and easy to fix.\n\nString coercion of a schism collection is identical to that of a\nClojure collection, e.g. a convergent set will coerce to a string as\n`#{:a :b :c}`. However, `pr-str` will generate a longer\ntagged literal containing all synchronization data for the structure\nto operate correctly. Schism eagerly enables edn readers for its\nstructures, so synchronization can be as simple as sending the results\nof `pr-str` over the wire, reading on the receiving side and\nconverging with the receiver's local copy.\n\nConvergence is done with `schism.core/converge`. It accepts two\ncollections, which it assumes are both copies of the same replicated\ncollection, and returns a single collection: the result of\nconvergence. Converge replicates the metadata of its first argument\ninto its return value, if present.\n\nEach CLJ and CLJS process working with any shared schism collection is\na node in a distributed computing cluster. Each node should have an\nidentity in order for synchronization to operate correctly. You may\ninvoke `schism.core/initialize-node!` with no arguments to initialize\nthe current process to have a randomly generated UUID as its node\nidentifier. You may use any serializable value as the node identifier\nby passing that value to initialize-node. If you can create stable\nnode identifiers, that can lead to some minor reduction in the storage\nrequirements for schism's data structures. If two nodes operate on a\nreplicated collection independently and do NOT have distinct node\nidentifiers, schism's convergence behavior is undefined. (Don't do\nthis). If you do not explicitly invoke `initialize-node!`, it is left\nat the value `nil`.\n\n## Nested collections\n\nIt is common to build up a tree of maps and vectors to create several\naddressable values that cohere to a common whole. If one built this\ncollection up out of individual schism collections, a number of\nproblems with convergence and serialization would present\nthemselves. I have provided `schism.core/nested-map` and\n`schism.core/nested-vector` to address both these problems. While\nthese provide best-least-surprise convergence, it's important to\nunderstand the limitations and leverages of best:\n\n- These collections incur substantially more CPU time to conduct\n  simple operations as a isomorphic mirror of all modifications must\n  be computed on each update.\n- While adding empty collections should work, please avoid doing so.\n- `clojure.core/assoc-in`, `clojure.core/update-in`, and friends\n  should work with the convergent map without any special handling.\n- Vectors containing leaf nodes will compose tail insertions, so two\n  nodes adding to the tail with `conj` will have both of their\n  additions retained in chronological order.\n- Vectors at intermediate nodes will treat the child at the index as\n  identity.\n- Collections must be isomorphic to converge: Do not allow one node to\n  place a vector and another node to place a map at the same path in\n  the tree.\n\nGiven the additional complications I strongly encourage clients to\npursue a strategy of retaining a shallow convergent-vector of entity\nids and one convergent-map for each entity. For those cases where this\ncombo is not sufficient, please wield `nested-map` and `nested-vector`\nwith care.\n\n## Further work\n\n- Configurable convergence\n- Other good ideas as the community provides them\n\n## Contributing\n\nI don't use CA's or other such things. Bugfixes are welcome and\nappreciated.\n\nI reserve the right to dismiss feature requests in the guise of PRs.\n\nAll work will be evaluated in light of conformance with motivations as\nstated in this document.\n\n## License\n\nCopyright © 2020 Alex Redington\n\nDistributed under the MIT License\n"
  },
  {
    "path": "project.clj",
    "content": "(defproject com.holychao/schism \"0.1.2\"\n  :description \"First Class CRDTs for Clojure\"\n  :url \"https://github.com/aredington/schism\"\n  :license {:name \"MIT License\"\n            :url \"https://opensource.org/licenses/MIT\"}\n  :dependencies [[org.clojure/clojure \"1.10.0\" :scope \"provided\"]\n                 [org.clojure/clojurescript \"1.10.520\" :scope \"provided\"]]\n  :plugins [[lein-cljsbuild \"1.1.7\"]\n            [lein-doo \"0.1.11\"]]\n  :profiles {:dev {:dependencies [[doo \"0.1.11\"]\n                                  [org.clojure/test.check \"0.10.0-alpha4\"]]}}\n  :cljsbuild {:builds [{:id \"test\"\n                        :source-paths [\"src\" \"test\"]\n                        :compiler {:output-to     \"target/test.js\"\n                                   :main schism.test\n                                   :output-dir    \"target\"\n                                   :optimizations :none\n                                   :source-map    true\n                                   :pretty-print  true\n                                   :recompile-dependents false\n                                   :parallel-build true\n                                   :checked-arrays :warn}}]}\n  :clean-targets ^{:protect false} [\"target\"]\n  :aliases {\"test-platforms\" [\"do\" \"clean,\" \"test,\" \"doo\" \"chrome-headless\" \"test\" \"once\"]})\n"
  },
  {
    "path": "resources/data_readers.cljc",
    "content": "{schism/set           schism.impl.types.set/read-edn-set\n schism/map           schism.impl.types.map/read-edn-map\n schism/list          schism.impl.types.list/read-edn-list\n schism/vector        schism.impl.types.vector/read-edn-vector\n schism/nested-map    schism.impl.types.nested-map/read-edn-map\n schism/nested-vector schism.impl.types.nested-vector/read-edn-vector}\n"
  },
  {
    "path": "src/schism/core.cljc",
    "content": "(ns schism.core\n  (:require [schism.impl.types.set :as sset]\n            [schism.impl.types.map :as smap]\n            [schism.impl.types.list :as slist]\n            [schism.impl.types.vector :as svector]\n            [schism.impl.types.nested-map :as nmap]\n            [schism.impl.types.nested-vector :as nvector]\n            [schism.impl.protocols :as sp]\n            [schism.node :as sn]))\n\n(defn convergent-set\n  \"Create a new ORSWOT containing args. Each arg will be recorded as\n  being added to the set by the current node at invocation time.\"\n  [& args]\n  (apply sset/new-set args))\n\n(defn convergent-map\n  \"Create a new ORMWOT, establishing associations between each pair of\n  key-value arguments. Each entry will be recorded as being added to\n  the map by the current node at invocation time.\"\n  [& args]\n  (apply smap/new-map args))\n\n(defn convergent-list\n  \"Create a new convergent list containing args. Args will be placed\n  into the list in the order they appear during invocation, just as\n  with `clojure.core/list`. Each entry will be recorded as being added\n  to the list by the current node at invocation time.\"\n  [& args]\n  (apply slist/new-list args))\n\n(defn convergent-vector\n  \"Create a new convergent vector containing args. Each entry will be\n  recorded as being added to the list by the current node at\n  invocation time, in the ordinal position it occupied during\n  invocation.\"\n  [& args]\n  (apply svector/new-vector args))\n\n(defn nested-map\n  \"Create a new convergent, nesting map containing args. Each\n  non-collection value at any location in the map will be treated as a\n  single atomic value, allowing for discrete modification of each\n  terminal in the tree. Those collections returned by `nested-map`\n  cannot perform as well as those returned by `convergent-map` and\n  have higher computational and storage costs.\"\n  [& args]\n  (apply nmap/new-map args))\n\n(defn nested-vector\n  \"Create a new convergent, nesting vector containing args. Each\n  non-collection value at any location in the vector will be treated\n  as a single atomic value, allowing for discrete modification of each\n  terminal in the tree. Those collections returned by `nested-vector`\n  cannot perform as well as those returned by `convergent-vector` and\n  have higher computational and storage costs.\"\n  [& args]\n  (apply nvector/new-vector args))\n\n(defn converge\n  \"Return a converged copy of `c1` containing the modifications of\n  `c2`. Convergence is defined on a per-type basis. If `c1` has\n  metadata, retain that metadata on the returned result. Convergence\n  ticks the vector clock for the node on which convergence is\n  occurring. `c1` and `c2` must be collections of the same type.\n\n  The behavior of `converge` is not defined when either:\n\n  - The current value of `schism.node/*current-node*` is nil\n\n  - The current value of `schism.node/*current-node*` is shared with\n  another node making modifications to the same logical collection.\"\n  [c1 c2]\n  (sp/synchronize c1 c2))\n\n(def initialize-node!\n  \"Initialize the current node to an edn serializable value if\n  provided. If invoked with no argument, initializes the current node\n  to a random UUID.\"\n  sn/initialize-node!)\n\n(defmacro with-node\n  \"Run `body` with the current node set to `node-id`\"\n  [id & body]\n  `(sn/with-node ~id ~@body))\n"
  },
  {
    "path": "src/schism/impl/core.cljc",
    "content": "(ns schism.impl.core\n  (:require [schism.node :as node]\n            [clojure.set :as set])\n  #?(:clj (:import (java.util Date))))\n\n(def to-millis (memfn ^Date getTime))\n\n(defn to-date\n  [millis]\n  #?(:clj (Date. millis)\n     :cljs (js/Date. millis)))\n\n(defn now\n  []\n  #?(:clj (Date.)\n     :cljs (js/Date.)))\n\n(defn node-and-threshold\n  [data]\n  (->> data\n       :vector-clock\n       (reduce-kv (fn [[node time] candidate-node candidate-time]\n                    (if (< (to-millis time) (to-millis candidate-time))\n                      [candidate-node candidate-time]\n                      [node time]))\n                   (-> data :vector-clock first))\n       (#(update % 1 to-millis))))\n\n(defn retain-elements\n  \"Accepts two maps of the form\n\n  {:vector-clock <map of nodes to update times>\n   :elements <vector of {:data <opaque value>\n                         :author-node <node-id>\n                         :record-time <Date object>}>}\n\n  Returns a seq of elements to be retained using ORSWOT merge semantics.\"\n  [own-data other-data]\n  (let [other-threshold (-> own-data :vector-clock (get node/*current-node*) to-millis)\n        [other-node own-threshold] (node-and-threshold other-data)\n        own-vclock-for-other (-> own-data :vector-clock (get other-node))\n        other-vclock-limiter (if own-vclock-for-other\n                               (fn [{:keys [record-time] :as element}]\n                                 (>= (to-millis own-vclock-for-other) (to-millis record-time)))\n                               (constantly true))\n        other-vclock-for-own (-> other-data :vector-clock (get node/*current-node*))\n        own-vclock-limiter (if other-vclock-for-own\n                             (fn [{:keys [record-time] :as element}]\n                               (>= (to-millis other-vclock-for-own) (to-millis record-time)))\n                             (constantly true))\n        other-additions (remove #(and (> other-threshold (to-millis (:record-time %)))\n                                      (other-vclock-limiter %)) (:elements other-data))\n        own-additions (remove #(and (> own-threshold (to-millis (:record-time %)))\n                                    (own-vclock-limiter %)) (:elements own-data))]\n    (concat other-additions own-additions)))\n\n(defn common-elements\n  \"Accepts maps of the form\n\n   {:vector-clock <map of nodes to update times>\n    :elements <vector of {:data <opaque value>\n                          :author-node <node-id>\n                          :record-time <Date object>}>}\n\n   Returns a vector of the common elements.\"\n  [& datasets]\n  (apply set/intersection (map (comp set :elements) datasets)))\n\n(defn distinct-data\n  \"Accepts maps of the form\n\n   {:vector-clock <map of nodes to update times>\n    :elements <vector of {:data <opaque value>\n                          :author-node <node-id>\n                          :record-time <Date object>}>}\n\n   Returns a vector of the maps with the common elements entries removed.\"\n  [& datasets]\n  (let [common-elements (apply common-elements datasets)]\n    (->> datasets\n         (map #(update % :elements (fn [elements] (remove common-elements elements))))\n         (into []))))\n\n(defn merged-clock\n  \"Accepts a collection of elements, and two or more datasets of the form\n\n   {:vector-clock <map of nodes to update times>\n    :elements <vector of {:data <opaque value>\n                          :author-node <node-id>\n                          :record-time <Date object>}>}\n\n   Returns a vector clock of the relevant nodes, that being the nodes\n  referenced as :author-node in elements.\"\n  [elements & datasets]\n  (let [relevant-nodes (set (map :author-node elements))]\n    (-> (apply merge-with (partial max-key to-millis) (map :vector-clock datasets))\n        (select-keys relevant-nodes))))\n\n(def tail-insertion-sort-value\n  \"The value to use when sorting insertions by index, and the recorded\n  index was -1, indicating the element was inserted at the tail.\"\n  #?(:clj Long/MAX_VALUE\n     :cljs (.-MAX_SAFE_INTEGER js/Number)))\n\n(defn assoc-n-with-tail-support\n  \"Assoc with support to place `v` at the tail of `a` when `n` is -1.\"\n  [a n v]\n  (if (= n -1)\n    (conj a v)\n    (assoc a n v)))\n"
  },
  {
    "path": "src/schism/impl/protocols.cljc",
    "content": "(ns schism.impl.protocols)\n\n(defprotocol Convergent\n  (synchronize [convergent other]\n    \"Synchronizes `convergent` with `other` such that all changes\n    incorporated into `other` will be represented in a new persistent\n    structure derived from `convergent`.\"))\n\n(defprotocol Vclocked\n  \"A protocol for obtaining the current vector clock of a value, and\n  for deriving a new vector clock for a value.\"\n  (get-clock [clocked] \"Returns the current vector clock of `clocked`,\n  a map of node IDs to timestamps.\")\n  (with-clock [clocked clock] \"Returns a new structure derived from\n  `clocked`, associating `clock` with the returned value.\"))\n\n(extend-protocol Vclocked\n  nil\n  (get-clock [_] {})\n  (with-clock [_ clock] nil))\n"
  },
  {
    "path": "src/schism/impl/types/list.cljc",
    "content": "(ns schism.impl.types.list\n  \"Definition and support for Schism's Convergent List type. The\n  convergent list is a simple timestamped log of entries with a vector\n  clock. Convergence places entries into the resultant list in\n  insertion order. The vector clock conveys that an item has been\n  removed from the list on another node.\"\n  (:require [schism.impl.protocols :as proto]\n            [schism.impl.vector-clock :as vc]\n            [schism.impl.core :as ic]\n            [schism.node :as node]\n            #?(:cljs [cljs.reader :as reader]))\n  #?(:cljs (:require-macros [schism.impl.vector-clock :as vc]))\n  #?(:clj (:import (clojure.lang IPersistentCollection IPersistentStack IReduce Counted IHashEq Seqable IObj IMeta ISeq)\n                   (java.io Writer)\n                   (java.util Date Collection)\n                   (java.lang Object))))\n\n;; A CLJ & CLJS implementation of a convergent list\n\n;; Each list maintains its own vector clock, and insertion times and\n;; nodes for each element of the list. ConvergentList entries and\n;; insertions are correlated positionally (as the list may contain the\n;; same item multiple times.) Insertion times dictate ordering. The\n;; vector clock determines if an entry has been removed.\n\n(declare clist-conj clist-rest clist-empty)\n\n#?(:clj (deftype ConvergentList [data vclock insertions]\n          Counted\n          (count [this] (.count ^Counted (.-data this)))\n\n          IPersistentCollection\n          (cons [this o] (clist-conj this o))\n          (empty [this] (clist-empty this))\n          (equiv [this other] (.equiv ^IPersistentCollection (.-data this) other))\n\n          Object\n          (equals [this o]\n            (.equals (.-data this) o))\n          (hashCode [this]\n            (.hashCode (.-data this)))\n          (toString [this]\n            (.toString (.-data this)))\n\n          IHashEq\n          (hasheq [this]\n            (.hasheq ^IHashEq (.-data this)))\n\n          Seqable\n          (seq [this]\n            this)\n\n          java.util.List\n          (add [this o] (.add ^java.util.List (.-data this) o))\n          (add [this index o] (.add ^java.util.List (.-data this) index o))\n          (addAll [this c] (.addAll ^java.util.List (.-data this) c))\n          (clear [this] (.clear ^java.util.List (.-data this)))\n          (contains [this o] (.contains ^java.util.List (.-data this) o))\n          (containsAll [this c] (.containsAll ^java.util.List (.-data this) c))\n          (get [this i] (.get ^java.util.List (.-data this) i))\n          (indexOf [this o] (.indexOf ^java.util.List (.-data this) o))\n          (isEmpty [this] (.isEmpty ^java.util.List (.-data this)))\n          (iterator [this] (.iterator ^java.util.List (.-data this)))\n          (lastIndexOf [this o] (.lastIndexOf ^java.util.List (.-data this) o))\n          (listIterator [this] (.listIterator ^java.util.List (.-data this)))\n          (^Object remove [this ^int i] (.remove ^java.util.List (.-data this) i))\n          (^boolean remove [this ^Object o] (.remove ^java.util.List (.-data this) o))\n          (removeAll [this c] (.removeAll ^java.util.List (.-data this) c))\n          (replaceAll [this op] (.replaceAll ^java.util.List (.-data this) op))\n          (retainAll [this c] (.retainAll ^java.util.List (.-data this) c))\n          (set [this i e] (.set ^java.util.List (.-data this) i e))\n          (size [this] (.size ^java.util.List (.-data this)))\n          (sort [this c] (.sort ^java.util.List (.-data this) c))\n          (spliterator [this] (.spliterator ^java.util.List (.-data this)))\n          (subList [this i j] (.subList ^java.util.List (.-data this) i j))\n          (toArray [this] (.toArray ^java.util.List (.-data this)))\n          (^\"[Ljava.lang.Object;\" toArray [this ^\"[Ljava.lang.Object;\" a]\n           (.toArray ^java.util.List (.-data this) a))\n\n          IObj\n          (withMeta [this meta]\n            (ConvergentList. (with-meta ^IObj (.-data this)\n                               meta)\n                             (.-vclock this)\n                             (.-insertions this)))\n\n          IMeta\n          (meta [this]\n            (.meta ^IMeta (.-data this)))\n\n          IReduce\n          (reduce [this f]\n            (.reduce ^IReduce (.-data this) f))\n\n          IPersistentStack\n          (peek [this] (.peek ^IPersistentStack (.-data this)))\n          (pop [this]\n            (clist-rest this))\n\n          ISeq\n          (first [this] (.first ^ISeq (.-data this)))\n          (next [this] (clist-rest this))\n          (more [this] (clist-rest this)))\n   :cljs (deftype ConvergentList [data vclock insertions]\n           ICounted\n           (-count [this] (-count (.-data this)))\n\n           IEmptyableCollection\n           (-empty [this] (clist-empty this))\n\n           ICollection\n           (-conj [this o] (clist-conj this o))\n\n           IEquiv\n           (-equiv [this other]\n             (-equiv (.-data this) other))\n\n           IPrintWithWriter\n           (-pr-writer [o writer opts]\n             (-write writer \"#schism/list [\")\n             (-write writer (pr-str (.-data o)))\n             (-write writer \", \")\n             (-write writer (pr-str (.-vclock o)))\n             (-write writer \", \")\n             (-write writer (pr-str (.-insertions o)))\n             (-write writer \"]\"))\n\n           IHash\n           (-hash [this] (-hash (.-data this)))\n\n           ISeqable\n           (-seq [this] this)\n\n           Object\n           (toString [this] (.toString (.-data this)))\n\n           IMeta\n           (-meta [this]\n             (-meta (.-data this)))\n\n           IWithMeta\n           (-with-meta [this meta]\n             (ConvergentList. (-with-meta (.-data this)\n                                          meta)\n                              (.-vclock this)\n                              (.-insertions this)))\n\n           ISeq\n           (-first [this]\n             (-first (.-data this)))\n           (-rest [this]\n             (clist-rest this))))\n\n(defn clist-conj [^ConvergentList clist o]\n  (vc/update-clock now clist\n                   (ConvergentList. (conj (.-data clist) o)\n                                    (.-vclock clist)\n                                    (conj (.-insertions clist) [node/*current-node* now]))))\n\n(defn clist-empty [^ConvergentList clist]\n  (vc/update-clock _ clist\n                   (ConvergentList. (list)\n                                    (hash-map)\n                                    (list))))\n\n(defn clist-rest [^ConvergentList clist]\n  (vc/update-clock _ clist\n                   (ConvergentList. (rest (.-data clist))\n                                    (.-vclock clist)\n                                    (rest (.-insertions clist)))))\n\n(defn- elemental-data\n  [^ConvergentList l]\n  {:vector-clock (.-vclock l)\n   :elements (into []\n                   (for [[datum [author-node record-time]] (map vector (.-data l) (.-insertions l))]\n                     {:data datum\n                      :author-node author-node\n                      :record-time record-time}))})\n\n(extend-type ConvergentList\n  proto/Vclocked\n  (get-clock [this] (.-vclock this))\n  (with-clock [this new-clock] (ConvergentList. (.-data this)\n                                                new-clock\n                                                (.-insertions this)))\n\n  proto/Convergent\n  (synchronize [this ^ConvergentList other]\n    (let [own-meta (-> this .-data meta)\n          own-data (elemental-data this)\n          other-data (elemental-data other)\n          retain (->> (reverse (:elements other-data))\n                      (map vector (reverse (:elements own-data)))\n                      (take-while (partial apply =))\n                      reverse\n                      (map first))\n          completed-elements (concat (sort-by :record-time (apply ic/retain-elements\n                                                                  (ic/distinct-data own-data other-data)))\n                                     retain)\n          completed-data (->> completed-elements\n                              (map :data)\n                              (into '())\n                              reverse)\n          completed-insertions (->> completed-elements\n                                    (map (fn [{:keys [author-node record-time]}]\n                                           [author-node record-time]))\n                                    (into '())\n                                    reverse)\n          completed-vclock (ic/merged-clock completed-elements own-data other-data)]\n      (vc/update-clock _ this\n                       (ConvergentList. (with-meta completed-data own-meta)\n                                        completed-vclock\n                                        completed-insertions)))))\n\n#?(:clj (defmethod print-method ConvergentList\n          [^ConvergentList l ^Writer writer]\n          (.write writer \"#schism/list [\")\n          (.write writer (pr-str (.-data l)))\n          (.write writer \", \")\n          (.write writer (pr-str (.-vclock l)))\n          (.write writer \", \")\n          (.write writer (pr-str (.-insertions l)))\n          (.write writer \"]\")))\n\n(defn read-edn-list\n  [read-object]\n  (let [[data vclock insertions] read-object]\n    (ConvergentList. data vclock insertions)))\n\n#?(:cljs (cljs.reader/register-tag-parser! 'schism/list read-edn-list))\n\n(defn new-list\n  ([] (ConvergentList. (list)\n                       (hash-map)\n                       (list)))\n  ([& args] (vc/update-clock now nil\n                             (ConvergentList. (apply list args)\n                                              (hash-map)\n                                              (apply list (repeat (count args) [node/*current-node* now]))))))\n"
  },
  {
    "path": "src/schism/impl/types/map.cljc",
    "content": "(ns schism.impl.types.map\n  \"Definition and support for Schism's Convergent Map type, an ORMWOT\n  implemented on top of Clojure's persistent maps and a Schism Vector\n  Clock.\"\n  (:require [schism.impl.protocols :as proto]\n            [schism.impl.vector-clock :as vc]\n            [schism.impl.core :as ic]\n            [schism.node :as node]\n            [clojure.set :as set]\n            #?(:cljs [cljs.reader :as reader]))\n  #?(:cljs (:require-macros [schism.impl.vector-clock :as vc]))\n  #?(:clj (:import (clojure.lang IPersistentCollection IPersistentMap IHashEq Associative ILookup Counted Seqable IMapIterable IKVReduce IFn IObj IMeta)\n                   (java.io Writer)\n                   (java.util Date Collection)\n                   (java.lang Object))))\n\n;; A CLJS and CLJ imeplementation of ORMWOT (Observed-Removed Map without Tombstones)\n\n;; Each map maintains its own vector clock, and birth dots for each\n;; map entry by key. A birth dot consists of the node adding the\n;; entry, and the date at which it was added. The vector clock\n;; determines if an entry has been removed: a compared map absent an\n;; entry, but with a newer vector clock than the own birthdot on the\n;; entry indicates that it was removed; a compared map absent an entry\n;; with an older vector clock indicates that the birthdot was never\n;; seen and should be in the merged map. Birthdots also arbitrate\n;; entries: for each entry the last writer wins.\n\n(declare ormwot-conj ormwot-empty ormwot-assoc ormwot-dissoc)\n\n(def not-found-sym (gensym :not-found))\n\n#?(:clj (deftype Map [data vclock birth-dots]\n          Counted\n          (count [this] (.count ^Counted (.-data this)))\n\n          IPersistentCollection\n          (cons [this o] (ormwot-conj this o))\n          (empty [this] (ormwot-empty this))\n          (equiv [this other] (.equiv ^IPersistentCollection (.-data this) other))\n\n          IPersistentMap\n          (assoc [this k v] (ormwot-assoc this k v))\n          (assocEx [this k v]\n            (when (not= (get this k not-found-sym) not-found-sym)\n              (throw (ex-info \"Attempt to assocEx on map with key\" {:map this\n                                                                    :key k\n                                                                    :value v})))\n            (ormwot-assoc this k v))\n          (without [this k] (ormwot-dissoc this k))\n\n          Object\n          (equals [this o]\n            (.equals (.-data this) o))\n          (hashCode [this]\n            (.hashCode (.-data this)))\n          (toString [this]\n            (.toString (.-data this)))\n\n          ILookup\n          (valAt [this k]\n            (.valAt ^ILookup (.-data this) k))\n          (valAt [this k not-found]\n            (.valAt ^ILookup (.-data this) k not-found))\n\n          IMapIterable\n          (keyIterator [this]\n            (.keyIterator ^IMapIterable (.-data this)))\n          (valIterator [this]\n            (.valIterator ^IMapIterable (.-data this)))\n\n          IKVReduce\n          (kvreduce [this f init]\n            (.kvreduce ^IKVReduce (.-data this) f init))\n\n          IHashEq\n          (hasheq [this]\n            (.hasheq ^IHashEq (.-data this)))\n\n          Seqable\n          (seq [this]\n            (.seq ^Seqable (.-data this)))\n\n          java.util.Map\n          (clear [this] (.clear ^java.util.Map (.-data this)))\n          (compute [this k f] (.compute ^java.util.Map (.-data this) k f))\n          (computeIfAbsent [this k f] (.computeIfAbsent ^java.util.Map (.-data this) k f))\n          (computeIfPresent [this k f] (.computeIfPresent ^java.util.Map (.-data this) k f))\n          (containsKey [this k] (.containsKey ^java.util.Map (.-data this) k))\n          (containsValue [this v] (.containsValue ^java.util.Map (.-data this) v))\n          (entrySet [this] (.entrySet ^java.util.Map (.-data this)))\n          (get [this k] (.get ^java.util.Map (.-data this) k))\n          (getOrDefault [this k not-found] (.getOrDefault ^java.util.Map (.-data this) k not-found))\n          (isEmpty [this] (.isEmpty ^java.util.Map (.-data this)))\n          (keySet [this] (.keySet ^java.util.Map (.-data this)))\n          (merge [this k v f] (.merge ^java.util.Map (.-data this) k v f))\n          (put [this k v] (.put ^java.util.Map (.-data this) k v))\n          (putAll [this m] (.putAll ^java.util.Map (.-data this) m))\n          (putIfAbsent [this k v] (.putIfAbsent ^java.util.Map (.-data this) k v))\n          (remove [this k] (.remove ^java.util.Map (.-data this) k))\n          (remove [this k v] (.remove ^java.util.Map (.-data this) k v))\n          (replace [this k v] (.replace ^java.util.Map (.-data this) k v))\n          (replace [this k ov nv] (.replace ^java.util.Map (.-data this) k ov nv))\n          (replaceAll [this f] (.replaceAll ^java.util.Map (.-data this) f))\n          (size [this] (.size ^java.util.Map (.-data this)))\n          (values [this] (.values ^java.util.Map (.-data this)))\n\n          IFn\n          (invoke [this k]\n            (.invoke ^IFn (.-data this) k))\n          (invoke [this k not-found]\n            (.invoke ^IFn (.-data this) k not-found))\n\n          IObj\n          (withMeta [this meta]\n            (Map. (with-meta ^IObj (.-data this)\n                    meta)\n                  (.-vclock this) (.-birth-dots this)))\n\n          IMeta\n          (meta [this]\n            (.meta ^IMeta (.-data this))))\n   :cljs (deftype Map [data vclock birth-dots]\n           ICounted\n           (-count [this] (-count (.-data this)))\n\n           IEmptyableCollection\n           (-empty [this] (ormwot-empty this))\n\n           ICollection\n           (-conj [this o] (ormwot-conj this o))\n\n           IAssociative\n           (-contains-key? [this k]\n             (-contains-key? (.-data this) k))\n           (-assoc [this k v]\n             (ormwot-assoc this k v))\n\n           IFind\n           (-find [this k]\n             (-find (.-data this) k))\n\n           IMap\n           (-dissoc [this k]\n             (ormwot-dissoc this k))\n\n           IKVReduce\n           (-kv-reduce [this f init]\n             (-kv-reduce (.-data this) f init))\n\n           IEquiv\n           (-equiv [this other]\n             (-equiv (.-data this) other))\n\n           ILookup\n           (-lookup [this o]\n             (-lookup (.-data this) o))\n           (-lookup [this o not-found]\n             (-lookup (.-data this) o not-found))\n\n           IPrintWithWriter\n           (-pr-writer [o writer opts]\n             (-write writer \"#schism/map [\")\n             (-write writer (pr-str (.-data o)))\n             (-write writer \", \")\n             (-write writer (pr-str (.-vclock o)))\n             (-write writer \", \")\n             (-write writer (pr-str (.-birth-dots o)))\n             (-write writer \"]\"))\n\n           IHash\n           (-hash [this] (-hash (.-data this)))\n\n           IFn\n           (-invoke [this o] ((.-data this) o))\n           (-invoke [this o not-found] ((.-data this) o not-found))\n\n           ISeqable\n           (-seq [this] (-seq (.-data this)))\n\n           Object\n           (toString [this] (.toString (.-data this)))\n\n           IMeta\n           (-meta [this]\n             (-meta (.-data this)))\n\n           IWithMeta\n           (-with-meta [this meta]\n             (Map. (-with-meta (.-data this)\n                               meta)\n                   (.-vclock this)\n                   (.-birth-dots this)))))\n\n(defn ormwot-conj\n  [^Map ormwot pair]\n  (vc/update-clock now ormwot\n                   (Map. (conj (.-data ormwot) pair)\n                         (.-vclock ormwot)\n                         (assoc (.-birth-dots ormwot) (first pair) [node/*current-node* now]))))\n\n(defn ormwot-empty\n  [^Map ormwot]\n  (vc/update-clock _ ormwot\n                   (Map. (hash-map)\n                         (hash-map)\n                         (hash-map))))\n\n(defn ormwot-assoc\n  [^Map ormwot k v]\n  (vc/update-clock now ormwot\n                   (Map. (assoc (.-data ormwot) k v)\n                         (.-vclock ormwot)\n                         (assoc (.-birth-dots ormwot) k [node/*current-node* now]))))\n\n(defn ormwot-dissoc\n  [^Map ormwot k]\n  (vc/update-clock _ ormwot\n                   (Map. (dissoc (.-data ormwot) k)\n                         (.-vclock ormwot)\n                         (dissoc (.-birth-dots ormwot) k))))\n\n(defn- elemental-data\n  [^Map m]\n  {:vector-clock (.-vclock m)\n   :elements (into []\n                   (for [datum (.-data m)]\n                     (let [dot (get (.-birth-dots m) (key datum))]\n                       {:data datum\n                        :author-node (first dot)\n                        :record-time (last dot)})))})\n\n(extend-type Map\n  proto/Vclocked\n  (get-clock [this] (.-vclock this))\n  (with-clock [this new-clock] (Map. (.-data this)\n                                     new-clock\n                                     (.-birth-dots this)))\n\n  proto/Convergent\n  (synchronize [this ^Map other]\n    (let [own-meta (-> this .-data meta)\n          own-data (elemental-data this)\n          other-data (elemental-data other)\n          retain (filter (ic/common-elements own-data other-data)\n                         (:elements own-data))\n          completed-elements (->> (apply ic/retain-elements\n                                         (ic/distinct-data own-data other-data))\n                                  (concat retain)\n                                  (sort-by :record-time))\n          completed-data (into {} (map :data completed-elements))\n          completed-birth-dots (->> completed-elements\n                                    (map (fn [{:keys [data author-node record-time]}]\n                                           [(key data) [author-node record-time]]))\n                                    (into {}))\n          completed-vclock (ic/merged-clock completed-elements own-data other-data)]\n      (vc/update-clock _ this\n                       (Map. (with-meta completed-data\n                               own-meta)\n                             completed-vclock\n                             completed-birth-dots)))))\n\n#?(:clj (defmethod print-method Map\n          [^Map m ^Writer writer]\n          (.write writer \"#schism/map [\")\n          (.write writer (pr-str (.-data m)))\n          (.write writer \", \")\n          (.write writer (pr-str (.-vclock m)))\n          (.write writer \", \")\n          (.write writer (pr-str (.-birth-dots m)))\n          (.write writer \"]\")))\n\n(defn read-edn-map\n  [read-object]\n  (let [[data vclock birth-dots] read-object]\n    (Map. data vclock birth-dots)))\n\n#?(:cljs (cljs.reader/register-tag-parser! 'schism/map read-edn-map))\n\n(defn new-map\n  ([] (Map. (hash-map)\n            (hash-map)\n            (hash-map)))\n  ([& args] (vc/update-clock now nil\n                             (Map. (apply hash-map args)\n                                   (hash-map)\n                                   (apply hash-map\n                                          (mapcat (fn [[k _]]\n                                                    [k [node/*current-node* now]])\n                                                  (partition 2 args)))))))\n"
  },
  {
    "path": "src/schism/impl/types/nested_map.cljc",
    "content": "(ns schism.impl.types.nested-map\n  \"Definition and support for Schism's Deeply Convergent Map type, an\n  ORMWOT implemented on top of Clojure's persistent maps and a Schism\n  Vector Clock. Contrasted with schism.impl.types.map/Map, this incurs\n  substantially greater computational costs for assoc type operations\n  and cannot guarantee linear time results.\"\n  (:require [schism.impl.types.nesting-util :as nu]\n            [schism.impl.protocols :as proto]\n            [schism.impl.vector-clock :as vc]\n            [schism.impl.core :as ic]\n            [schism.node :as node]\n            [clojure.set :as set]\n            [clojure.data :refer [diff]]\n            #?(:cljs [cljs.reader :as reader]))\n  #?(:cljs (:require-macros [schism.impl.vector-clock :as vc]))\n  #?(:clj (:import (clojure.lang IPersistentCollection IPersistentMap IHashEq Associative ILookup Counted Seqable IMapIterable IKVReduce IFn IObj IMeta)\n                   (java.io Writer)\n                   (java.util Date Collection)\n                   (java.lang Object))))\n\n(declare nested-map-conj nested-map-empty nested-map-assoc nested-map-dissoc)\n\n(def not-found-sym (gensym :not-found))\n\n#?(:clj (deftype NestedMap [data vclock birth-dots]\n          Counted\n          (count [this] (.count ^Counted (.-data this)))\n\n          IPersistentCollection\n          (cons [this o] (nested-map-conj this o))\n          (empty [this] (nested-map-empty this))\n          (equiv [this other] (.equiv ^IPersistentCollection (.-data this) other))\n\n          IPersistentMap\n          (assoc [this k v] (nested-map-assoc this k v))\n          (assocEx [this k v]\n            (when (not= (get this k not-found-sym) not-found-sym)\n              (throw (ex-info \"Attempt to assocEx on map with key\" {:map this\n                                                                    :key k\n                                                                    :value v})))\n            (nested-map-assoc this k v))\n          (without [this k] (nested-map-dissoc this k))\n\n          Object\n          (equals [this o]\n            (.equals (.-data this) o))\n          (hashCode [this]\n            (.hashCode (.-data this)))\n          (toString [this]\n            (.toString (.-data this)))\n\n          ILookup\n          (valAt [this k]\n            (.valAt ^ILookup (.-data this) k))\n          (valAt [this k not-found]\n            (.valAt ^ILookup (.-data this) k not-found))\n\n          IMapIterable\n          (keyIterator [this]\n            (.keyIterator ^IMapIterable (.-data this)))\n          (valIterator [this]\n            (.valIterator ^IMapIterable (.-data this)))\n\n          IKVReduce\n          (kvreduce [this f init]\n            (.kvreduce ^IKVReduce (.-data this) f init))\n\n          IHashEq\n          (hasheq [this]\n            (.hasheq ^IHashEq (.-data this)))\n\n          Seqable\n          (seq [this]\n            (.seq ^Seqable (.-data this)))\n\n          java.util.Map\n          (clear [this] (.clear ^java.util.Map (.-data this)))\n          (compute [this k f] (.compute ^java.util.Map (.-data this) k f))\n          (computeIfAbsent [this k f] (.computeIfAbsent ^java.util.Map (.-data this) k f))\n          (computeIfPresent [this k f] (.computeIfPresent ^java.util.Map (.-data this) k f))\n          (containsKey [this k] (.containsKey ^java.util.Map (.-data this) k))\n          (containsValue [this v] (.containsValue ^java.util.Map (.-data this) v))\n          (entrySet [this] (.entrySet ^java.util.Map (.-data this)))\n          (get [this k] (.get ^java.util.Map (.-data this) k))\n          (getOrDefault [this k not-found] (.getOrDefault ^java.util.Map (.-data this) k not-found))\n          (isEmpty [this] (.isEmpty ^java.util.Map (.-data this)))\n          (keySet [this] (.keySet ^java.util.Map (.-data this)))\n          (merge [this k v f] (.merge ^java.util.Map (.-data this) k v f))\n          (put [this k v] (.put ^java.util.Map (.-data this) k v))\n          (putAll [this m] (.putAll ^java.util.Map (.-data this) m))\n          (putIfAbsent [this k v] (.putIfAbsent ^java.util.Map (.-data this) k v))\n          (remove [this k] (.remove ^java.util.Map (.-data this) k))\n          (remove [this k v] (.remove ^java.util.Map (.-data this) k v))\n          (replace [this k v] (.replace ^java.util.Map (.-data this) k v))\n          (replace [this k ov nv] (.replace ^java.util.Map (.-data this) k ov nv))\n          (replaceAll [this f] (.replaceAll ^java.util.Map (.-data this) f))\n          (size [this] (.size ^java.util.Map (.-data this)))\n          (values [this] (.values ^java.util.Map (.-data this)))\n\n          IFn\n          (invoke [this k]\n            (.invoke ^IFn (.-data this) k))\n          (invoke [this k not-found]\n            (.invoke ^IFn (.-data this) k not-found))\n\n          IObj\n          (withMeta [this meta]\n            (NestedMap. (with-meta ^IObj (.-data this)\n                          meta)\n                        (.-vclock this) (.-birth-dots this)))\n\n          IMeta\n          (meta [this]\n            (.meta ^IMeta (.-data this))))\n\n   :cljs (deftype NestedMap [data vclock birth-dots]\n           ICounted\n           (-count [this] (-count (.-data this)))\n\n           IEmptyableCollection\n           (-empty [this] (nested-map-empty this))\n\n           ICollection\n           (-conj [this o] (nested-map-conj this o))\n\n           IAssociative\n           (-contains-key? [this k]\n             (-contains-key? (.-data this) k))\n           (-assoc [this k v]\n             (nested-map-assoc this k v))\n\n           IFind\n           (-find [this k]\n             (-find (.-data this) k))\n\n           IMap\n           (-dissoc [this k]\n             (nested-map-dissoc this k))\n\n           IKVReduce\n           (-kv-reduce [this f init]\n             (-kv-reduce (.-data this) f init))\n\n           IEquiv\n           (-equiv [this other]\n             (-equiv (.-data this) other))\n\n           ILookup\n           (-lookup [this o]\n             (-lookup (.-data this) o))\n           (-lookup [this o not-found]\n             (-lookup (.-data this) o not-found))\n\n           IPrintWithWriter\n           (-pr-writer [o writer opts]\n             (-write writer \"#schism/nested-map [\")\n             (-write writer (pr-str (.-data o)))\n             (-write writer \", \")\n             (-write writer (pr-str (.-vclock o)))\n             (-write writer \", \")\n             (-write writer (pr-str (.-birth-dots o)))\n             (-write writer \"]\"))\n\n           IHash\n           (-hash [this] (-hash (.-data this)))\n\n           IFn\n           (-invoke [this o] ((.-data this) o))\n           (-invoke [this o not-found] ((.-data this) o not-found))\n\n           ISeqable\n           (-seq [this] (-seq (.-data this)))\n\n           Object\n           (toString [this] (.toString (.-data this)))\n\n           IMeta\n           (-meta [this]\n             (-meta (.-data this)))\n\n           IWithMeta\n           (-with-meta [this meta]\n             (NestedMap. (-with-meta (.-data this)\n                                     meta)\n                         (.-vclock this)\n                         (.-birth-dots this)))))\n\n(defn nested-map-conj\n  [^NestedMap nm pair]\n  (vc/update-clock now nm\n                   (let [[updated updated-dots] (nu/nested-update (.-data nm)\n                                                                  (.-birth-dots nm)\n                                                                  #(conj % pair)\n                                                                  node/*current-node*\n                                                                  now)]\n                     (NestedMap. updated\n                                 (.-vclock nm)\n                                 updated-dots))))\n\n(defn nested-map-empty\n  [^NestedMap nm]\n  (vc/update-clock _ nm\n                   (NestedMap. (hash-map)\n                               (hash-map)\n                               (hash-map))))\n\n(defn nested-map-assoc\n  [^NestedMap nm k v]\n  (vc/update-clock now nm\n                   (let [[updated updated-dots] (nu/nested-update (.-data nm)\n                                                                  (.-birth-dots nm)\n                                                                  #(assoc % k v)\n                                                                  node/*current-node*\n                                                                  now)]\n                     (NestedMap. updated\n                                 (.-vclock nm)\n                                 updated-dots))))\n\n(defn nested-map-dissoc\n  [^NestedMap nm k]\n  (vc/update-clock now nm\n                   (let [[updated updated-dots] (nu/nested-update (.-data nm)\n                                                                  (.-birth-dots nm)\n                                                                  #(dissoc % k)\n                                                                  node/*current-node*\n                                                                  now)]\n                     (NestedMap. updated\n                                 (.-vclock nm)\n                                 updated-dots))))\n\n(defn- elemental-data\n  [^NestedMap nm]\n\n  (let [flat-data (nu/flat (.-data nm))]\n    {:vector-clock (.-vclock nm)\n     :elements (into []\n                     (for [datum flat-data]\n                       (let [dot (get-in (.-birth-dots nm) (nu/access-path (key datum)))]\n                         {:data {:entry datum\n                                 :insert-index (:i dot)}\n                          :author-node (:a dot)\n                          :record-time (:t dot)})))}))\n\n(extend-type NestedMap\n  proto/Vclocked\n  (get-clock [this] (.-vclock this))\n  (with-clock [this new-clock] (NestedMap. (.-data this)\n                                           new-clock\n                                           (.-birth-dots this)))\n  proto/Convergent\n  (synchronize [this ^NestedMap other]\n    (let [own-meta (-> this .-data meta)\n          own-data (elemental-data this)\n          other-data (elemental-data other)\n          retain (filter (ic/common-elements own-data other-data)\n                         (:elements own-data))\n          completed-elements (->> (apply ic/retain-elements\n                                         (ic/distinct-data own-data other-data))\n                                  (concat retain)\n                                  (sort-by :record-time)\n                                  (map nu/finalize-projection-key))\n          completed-flat-data (map (comp :entry :data) completed-elements)\n          completed-flat-birth-dots (map (fn [{:keys [author-node record-time]\n                                               {:keys [insert-index entry] :as data} :data}]\n                                           (let [dot {:a author-node :t record-time}]\n                                             [(first entry) (if insert-index\n                                                              (assoc dot :i insert-index)\n                                                              dot)]))\n                                         completed-elements)\n          completed-vclock (ic/merged-clock completed-elements own-data other-data)]\n      (vc/update-clock _ this\n                       (NestedMap. (with-meta (nu/project completed-flat-data)\n                                     own-meta)\n                                   completed-vclock\n                                   (nu/project completed-flat-birth-dots))))))\n\n#?(:clj (defmethod print-method NestedMap\n          [^NestedMap nm ^Writer writer]\n          (.write writer \"#schism/nested-map [\")\n          (.write writer (pr-str (.-data nm)))\n          (.write writer \", \")\n          (.write writer (pr-str (.-vclock nm)))\n          (.write writer \", \")\n          (.write writer (pr-str (.-birth-dots nm)))\n          (.write writer \"]\")))\n\n(defn read-edn-map\n  [read-object]\n  (let [[data vclock birth-dots] read-object]\n    (NestedMap. data vclock birth-dots)))\n\n#?(:cljs (cljs.reader/register-tag-parser! 'schism/map read-edn-map))\n\n(defn new-map\n  ([] (NestedMap. (hash-map)\n                  (hash-map)\n                  (hash-map)))\n  ([& args] (vc/update-clock now nil\n                             (let [[updated updated-dots] (nu/nested-update {}\n                                                                            {}\n                                                                            (fn [_] (apply hash-map args))\n                                                                            node/*current-node*\n                                                                            now)]\n                               (NestedMap. updated\n                                           (hash-map)\n                                           updated-dots)))))\n"
  },
  {
    "path": "src/schism/impl/types/nested_vector.cljc",
    "content": "(ns schism.impl.types.nested-vector\n  \"Definition and support for Schism's Deeply Convergent Vector\n  type. The convergent vector is a timestamped log of entries with a\n  vector clock & insertion index. Convergence places entries into the\n  resultant vector in insertion order, with insertions occurring by\n  replaying insertions operations in order. The vector clock conveys\n  that an item has been removed from the vector on another node. This\n  variant provides rich support for serialization and convergence of\n  deeply nested structures, at the cost that all modification\n  operations take linear time instead of constant or log time.\"\n  (:require [schism.impl.types.nesting-util :as nu]\n            [schism.impl.protocols :as proto]\n            [schism.impl.vector-clock :as vc]\n            [schism.impl.core :as ic]\n            [schism.node :as node]\n            [clojure.set :as set]\n            [clojure.data :refer [diff]]\n            #?(:cljs [cljs.reader :as reader]))\n  #?(:cljs (:require-macros [schism.impl.vector-clock :as vc]))\n  #?(:clj (:import (clojure.lang IPersistentCollection IPersistentStack IPersistentVector Reversible IReduce IKVReduce Indexed Associative Counted IHashEq Seqable IObj IMeta IFn ILookup)\n                   (java.io Writer)\n                   (java.util Date Collection)\n                   (java.lang Object Long))))\n\n(declare nested-vector-conj nested-vector-pop nested-vector-empty nested-vector-assoc)\n\n#?(:clj (deftype NestedVector [data vclock insertions]\n          Counted\n          (count [this] (.count ^Counted (.-data this)))\n\n          IPersistentCollection\n          (cons [this o] (nested-vector-conj this o))\n          (empty [this] (nested-vector-empty this))\n          (equiv [this other] (.equiv ^IPersistentCollection (.-data this) other))\n\n          Object\n          (equals [this o]\n            (.equals (.-data this) o))\n          (hashCode [this]\n            (.hashCode (.-data this)))\n          (toString [this]\n            (.toString (.-data this)))\n\n          IHashEq\n          (hasheq [this]\n            (.hasheq ^IHashEq (.-data this)))\n\n          Seqable\n          (seq [this]\n            (seq ^Seqable (.-data this)))\n\n          java.util.List\n          (add [this o] (.add ^java.util.List (.-data this) o))\n          (add [this index o] (.add ^java.util.List (.-data this) index o))\n          (addAll [this c] (.addAll ^java.util.List (.-data this) c))\n          (clear [this] (.clear ^java.util.List (.-data this)))\n          (contains [this o] (.contains ^java.util.List (.-data this) o))\n          (containsAll [this c] (.containsAll ^java.util.List (.-data this) c))\n          (get [this i] (.get ^java.util.List (.-data this) i))\n          (indexOf [this o] (.indexOf ^java.util.List (.-data this) o))\n          (isEmpty [this] (.isEmpty ^java.util.List (.-data this)))\n          (iterator [this] (.iterator ^java.util.List (.-data this)))\n          (lastIndexOf [this o] (.lastIndexOf ^java.util.List (.-data this) o))\n          (listIterator [this] (.listIterator ^java.util.List (.-data this)))\n          (^Object remove [this ^int i] (.remove ^java.util.List (.-data this) i))\n          (^boolean remove [this ^Object o] (.remove ^java.util.List (.-data this) o))\n          (removeAll [this c] (.removeAll ^java.util.List (.-data this) c))\n          (replaceAll [this op] (.replaceAll ^java.util.List (.-data this) op))\n          (retainAll [this c] (.retainAll ^java.util.List (.-data this) c))\n          (set [this i e] (.set ^java.util.List (.-data this) i e))\n          (size [this] (.size ^java.util.List (.-data this)))\n          (sort [this c] (.sort ^java.util.List (.-data this) c))\n          (spliterator [this] (.spliterator ^java.util.List (.-data this)))\n          (subList [this i j] (.subList ^java.util.List (.-data this) i j))\n          (toArray [this] (.toArray ^java.util.List (.-data this)))\n          (^\"[Ljava.lang.Object;\" toArray [this ^\"[Ljava.lang.Object;\" a]\n           (.toArray ^java.util.List (.-data this) a))\n\n\n          IObj\n          (withMeta [this meta]\n            (NestedVector. (with-meta ^IObj (.-data this)\n                                 meta)\n                               (.-vclock this)\n                               (.-insertions this)))\n\n          IMeta\n          (meta [this]\n            (.meta ^IMeta (.-data this)))\n\n          IReduce\n          (reduce [this f]\n            (.reduce ^IReduce (.-data this) f))\n\n          IKVReduce\n          (kvreduce [this f init]\n            (.kvreduce ^IKVReduce (.-data this) f init))\n\n          IPersistentStack\n          (peek [this] (.peek ^IPersistentStack (.-data this)))\n          (pop [this]\n            (nested-vector-pop this))\n\n          IPersistentVector\n          (assocN [this i v]\n            (nested-vector-assoc this i v))\n\n          ILookup\n          (valAt [this k]\n            (.valAt (.-data this) k))\n          (valAt [this k not-found]\n            (.valAt (.-data this) k not-found))\n\n          Associative\n          (containsKey [this k]\n            (.containsKey ^Associative (.-data this) k))\n          (entryAt [this k]\n            (.entryAt ^Associative (.-data this) k))\n          (assoc [this k v]\n            (nested-vector-assoc this k v))\n\n          Indexed\n          (nth [this i]\n            (.indexed ^Indexed (.-data this) i))\n          (nth [this i not-found]\n            (.indexed ^Indexed (.-data this) i not-found))\n\n          IFn\n          (invoke [this k]\n            (.invoke ^IFn (.-data this) k))\n          (invoke [this k not-found]\n            (.invoke ^IFn (.-data this) k not-found)))\n   :cljs (deftype NestedVector [data vclock insertions]\n           ICounted\n           (-count [this] (-count (.-data this)))\n\n           IEmptyableCollection\n           (-empty [this] (nested-vector-empty this))\n\n           ICollection\n           (-conj [this o] (nested-vector-conj this o))\n\n           IEquiv\n           (-equiv [this other]\n             (-equiv (.-data this) other))\n\n           IPrintWithWriter\n           (-pr-writer [o writer opts]\n             (-write writer \"#schism/nested-vector [\")\n             (-write writer (pr-str (.-data o)))\n             (-write writer \", \")\n             (-write writer (pr-str (.-vclock o)))\n             (-write writer \", \")\n             (-write writer (pr-str (.-insertions o)))\n             (-write writer \"]\"))\n\n           IHash\n           (-hash [this] (-hash (.-data this)))\n\n           ISeqable\n           (-seq [this] (-seq (.-data this)))\n\n           Object\n           (toString [this] (.toString (.-data this)))\n\n           IMeta\n           (-meta [this]\n             (-meta (.-data this)))\n\n           IWithMeta\n           (-with-meta [this meta]\n             (NestedVector. (-with-meta (.-data this)\n                                            meta)\n                                (.-vclock this)\n                                (.-insertions this)))\n\n           IFn\n           (-invoke [this k]\n             ((.-data this) k))\n           (-invoke [this k not-found]\n             ((.-data this) k not-found))\n\n           IIndexed\n           (-nth [this n]\n             (-nth (.-data this) n))\n           (-nth [this n not-found]\n             (-nth (.-data this) n not-found))\n\n           ILookup\n           (-lookup [this k]\n             (-lookup (.-data this) k))\n           (-lookup [this k not-found]\n             (-lookup (.-data this) k not-found))\n\n           IAssociative\n           (-contains-key? [this k]\n             (-contains-key? (.-data this) k))\n           (-assoc [this k v]\n             (nested-vector-assoc this k v))\n\n           IFind\n           (-find [this k]\n             (-find (.-data this) k))\n\n           IStack\n           (-peek [this]\n             (-peek (.-data this)))\n           (-pop [this]\n             (nested-vector-pop this))\n\n           IVector\n           (-assoc-n [this n v]\n             (nested-vector-assoc this n v))\n\n           IReduce\n           (-reduce [this f]\n             (-reduce (.-data this) f))\n           (-reduce [this f start]\n             (-reduce (.-data this) f start))\n\n           IKVReduce\n           (-kv-reduce [this f init]\n             (-kv-reduce (.-data this) f init))))\n\n(defn nested-vector-conj [^NestedVector nvector o]\n  (vc/update-clock now nvector\n                   (let [[updated updated-dots] (nu/nested-update (.-data nvector)\n                                                                  (.-insertions nvector)\n                                                                  #(conj % o)\n                                                                  node/*current-node*\n                                                                  now)]\n                     (NestedVector. updated\n                                    (.-vclock nvector)\n                                    updated-dots))))\n\n(defn nested-vector-empty [^NestedVector nvector]\n  (vc/update-clock _ nvector\n                   (NestedVector. (vector)\n                                  (hash-map)\n                                  (vector))))\n\n(defn nested-vector-pop [^NestedVector nvector]\n  (vc/update-clock _ nvector\n                   (NestedVector. (pop (.-data nvector))\n                                  (.-vclock nvector)\n                                  (pop (.-insertions nvector)))))\n\n(defn nested-vector-assoc [^NestedVector nvector k v]\n  (vc/update-clock now nvector\n                   (let [[updated updated-dots] (nu/nested-update (.-data nvector)\n                                                                  (.-insertions nvector)\n                                                                  #(assoc % k v)\n                                                                  node/*current-node*\n                                                                  now)]\n                    (NestedVector. updated\n                                   (.-vclock nvector)\n                                   updated-dots))))\n\n(defn- elemental-data\n  [^NestedVector v]\n  (let [flat-data (nu/flat (.-data v))]\n    {:vector-clock (.-vclock v)\n     :elements (into []\n                     (for [datum flat-data]\n                       (let [dot (get-in (.-insertions v) (nu/access-path (key datum)))]\n                        {:data {:entry datum\n                                :insert-index (:i dot)}\n                         :author-node (:a dot)\n                         :record-time (:t dot)})))}))\n\n(extend-type NestedVector\n  proto/Vclocked\n  (get-clock [this] (.-vclock this))\n  (with-clock [this new-clock] (NestedVector. (.-data this)\n                                                  new-clock\n                                                  (.-insertions this)))\n\n  proto/Convergent\n  (synchronize [this ^NestedVector other]\n    (let [own-meta (-> this .-data meta)\n          own-data (elemental-data this)\n          other-data (elemental-data other)\n          retain (filter (ic/common-elements own-data other-data)\n                         (:elements own-data))\n          completed-elements (->> (apply ic/retain-elements\n                                         (ic/distinct-data own-data other-data))\n                                  (concat retain)\n                                  (sort-by :record-time)\n                                  (map nu/finalize-projection-key))\n          completed-flat-data (map (comp :entry :data) completed-elements)\n          completed-flat-insertions (map (fn [{:keys [author-node record-time]\n                                          {:keys [insert-index entry] :as data} :data}]\n                                      (let [dot {:a author-node :t record-time}]\n                                        [(first entry) (if insert-index\n                                                         (assoc dot :i insert-index)\n                                                         dot)]))\n                                       completed-elements)\n          completed-vclock (ic/merged-clock completed-elements own-data other-data)]\n      (vc/update-clock _ this\n                       (NestedVector. (with-meta (nu/project completed-flat-data) own-meta)\n                                      completed-vclock\n                                      (nu/project completed-flat-insertions))))))\n\n#?(:clj (defmethod print-method NestedVector\n          [^NestedVector v ^Writer writer]\n          (.write writer \"#schism/nested-vector [\")\n          (.write writer (pr-str (.-data v)))\n          (.write writer \", \")\n          (.write writer (pr-str (.-vclock v)))\n          (.write writer \", \")\n          (.write writer (pr-str (.-insertions v)))\n          (.write writer \"]\")))\n\n(defn read-edn-vector\n  [read-object]\n  (let [[data vclock insertions] read-object]\n    (NestedVector. data vclock insertions)))\n\n#?(:cljs (cljs.reader/register-tag-parser! 'schism/nested-vector read-edn-vector))\n\n(defn new-vector\n  ([] (NestedVector. (vector)\n                     (hash-map)\n                     (vector)))\n  ([& args] (vc/update-clock now nil\n                             (let [[updated updated-dots] (nu/nested-update []\n                                                                  []\n                                                                  #(into % args)\n                                                                  node/*current-node*\n                                                                  now)]\n                               (NestedVector. updated\n                                              (hash-map)\n                                              updated-dots)))))\n"
  },
  {
    "path": "src/schism/impl/types/nesting_util.cljc",
    "content": "(ns schism.impl.types.nesting-util\n  \"Utility functions for supporting nested collections.\"\n  (:require [clojure.data :refer [diff]]\n            [schism.impl.core :as ic]))\n\n\n(defn compare-paths\n  [[[a-type a-val :as first-a] & rest-a :as a] [[b-type b-val :as first-b] & rest-b :as b]]\n  (cond\n    (and (nil? a) (nil? b)) 0\n    (nil? a) -1\n    (nil? b) 1\n    (= first-a first-b) (recur rest-a rest-b)\n    (and (= a-type 's) (= b-type 'a)) -1\n    (and (= b-type 's) (= a-type 'a)) 1\n    (and (= a-type 's) (= b-type 's)) (compare a-val b-val)\n    (or rest-a rest-b) (recur rest-a rest-b)\n    :else (compare (hash a-val) (hash b-val))))\n\n(defn flat\n  \"Flattens a structure of associatives and sequentials to an\n  associative of paths to leaf values. Each step in a path will retain\n  both the type and the edge value, maps will be flattened to\n  associatives. Vectors, lists, and other seqs will be flattened to\n  sequentials.\"\n  [c]\n  (if (or (not (coll? c))\n          (empty? c))\n    c\n    (let [marker (if (map? c) 'a 's)\n          m (if (map? c)\n              c\n              (map-indexed (fn [i e] [i e]) c))]\n      (into {}\n            (mapcat (fn [[k v]]\n                      (let [child-flat (flat v)]\n                        (if (or (not (coll? child-flat))\n                                (empty? child-flat))\n                          [[[[marker k]] child-flat]]\n                          (for [[path element] child-flat]\n                            [(apply vector [marker k] path) element])))))\n            m))))\n\n(defn access-path\n  \"`flat` returns a reconstitution path of both type and key. This is\n  useful for reprojecting the flat version up into a nested structure,\n  but is not amenable to `get-in`, `assoc-in`, et al. `access-path` will\n  convert a reconstitution path into a more conventional access path.\"\n  [reconstitution-path]\n  (map second reconstitution-path))\n\n(defn- assoc*\n  [m [type key] v]\n  (let [m (if m\n            m\n            (cond\n              (= type 'a) {}\n              (= type 's) []))\n        assoc-fn (if (vector? m)\n                   ic/assoc-n-with-tail-support\n                   assoc)]\n    (assoc-fn m key v)))\n\n(defn- assoc-in*\n  [m [[type key :as kspec] & kspecs] v]\n  (if kspecs\n    (assoc* m kspec (assoc-in* (get m key) kspecs v))\n    (assoc* m kspec v)))\n\n(defn project\n  \"Constitutes a structure as produced by `flat` up into a nested\n  collection of maps and vectors. As all sequential items are coerced\n  to vectors, this is not reflexive of `flat`.\"\n  ([vals] (project vals nil))\n  ([vals basis]\n   (reduce (fn [m [k v]]\n             (assoc-in* m k v))\n           basis\n           vals)))\n\n(defn clean*\n  \"Remove the key at k and any empty parents above it.\"\n  [m [k & ks]]\n  (if ks\n    (let [cleaned (clean* (get m k) ks)]\n      (if (empty? cleaned)\n        (cond (vector? m) (pop m)\n              (map? m) (dissoc m k))\n        (assoc m k cleaned)))\n    (cond (vector? m) (pop m)\n          (map? m) (dissoc m k))))\n\n(defn nested-update\n  \"Does all of the book-keeping for nested map/vector combo data types.\n  `original` is the original data structure, `provenance` is the\n  original structure's provenance data, `update` is a update function\n  to progress original.\n  Returns positionally: the updated `original`, and, the updated `provenance`.\"\n  [original provenance update author timestamp]\n  (let [updated (update original)\n        original-vals-flat (flat original)\n        update-vals-flat (flat updated)\n        [deletions additions common] (diff original-vals-flat update-vals-flat)\n        provenance (reduce (fn [m [k v]]\n                             (if (contains? additions k)\n                               m\n                               (clean* m (access-path k))))\n                           provenance\n                           deletions)\n        addition-dots (for [[k v] (sort-by first compare-paths additions)]\n                        (let [to-vector? (= 's (first (last k)))\n                              distinct? (not (contains? deletions k))\n                              basis {:a author\n                                     :t timestamp}]\n                          [k (if to-vector?\n                               (merge basis {:i (if distinct? -1 (last (last k)))})\n                               basis)]))]\n    [updated (project addition-dots provenance)]))\n\n(defn finalize-projection-key\n  [m]\n  (let [{:keys [entry insert-index]} (:data m)]\n    (if insert-index\n      (assoc-in m [:data :entry]\n                [(conj (pop (key entry)) ['s insert-index]) (val entry)])\n      m)))\n"
  },
  {
    "path": "src/schism/impl/types/set.cljc",
    "content": "(ns schism.impl.types.set\n  \"Definition and support for Schism's Convergent Set type, an ORSWOT\n  implemented on top of Clojure's Persistent Set, Persistent Map and\n  Schism's Vector Clock.\"\n  (:require [schism.impl.protocols :as proto]\n            [schism.impl.vector-clock :as vc]\n            [schism.impl.core :as ic]\n            [schism.node :as node]\n            [clojure.set :as set]\n            #?(:cljs [cljs.reader :as reader]))\n  #?(:cljs (:require-macros [schism.impl.vector-clock :as vc]))\n  #?(:clj (:import (clojure.lang IPersistentCollection IPersistentSet IHashEq Counted Seqable RT IFn IObj IMeta)\n                   (java.io Writer)\n                   (java.util Date Collection)\n                   (java.lang Object))))\n\n;; A CLJS and CLJ implementation of ORSWOT (Observed-Removed Set without Tombstones)\n\n;; Each set maintains its own vector clock, and birth dots for each\n;; member. A birth dot consists of the node adding the member, and the\n;; date at which it was added. The vector clock determines if an\n;; element has been removed: a compared set absent an element but with\n;; a newer vector clock than the own birthdot on element indicates\n;; that it was removed; a compared set absent an element with an older\n;; vector clock indicates that the birthdot was never seen and should\n;; be in the merged set.\n\n;; Dot membership is always exactly the cardinality of the Set. Clock\n;; membership is at most the cardinality of the set, but can correctly\n;; synchronize with one more member than the total number of nodes in\n;; the birth-dots; if a Vclock indicates an element was removed, the\n;; node converging its changes can claim responsibility for removing\n;; the element in the merged set.\n\n;; TODO:\n\n;; With vector clocks AND birthdots it is possible to disambiguate the\n;; removal of an element from the post-replication addition of that\n;; element. When merging other, examine it's vector clock for each\n;; entry that is uniquely in own. If that entry's authoring node is\n;; present in the vector clock, and the timestamp in other's vector\n;; clock is less than the birth dot of own's entry, then retain the\n;; entry, as other never saw it and could not have removed it.\n\n(declare orswot-conj orswot-empty orswot-disj)\n\n#?(:clj (deftype Set [data vclock birth-dots]\n          Counted\n          (count [this] (.count ^Counted (.-data this)))\n\n          IPersistentCollection\n          (cons [this o] (orswot-conj this o))\n          (empty [this] (orswot-empty this))\n          (equiv [this other]\n            (.equiv ^IPersistentCollection (.-data this) other))\n\n          IPersistentSet\n          (disjoin [this o] (orswot-disj this o))\n          (contains [this o] (.contains ^IPersistentSet (.-data this) o))\n          (get [this o] (.get ^IPersistentSet (.-data this) o))\n\n          Object\n          (equals [this o]\n            (.equals (.-data this) o))\n          (hashCode [this]\n            (.hashCode (.-data this)))\n          (toString [this]\n            (.toString data))\n\n          IHashEq\n          (hasheq [this]\n            (.hasheq ^IHashEq (.-data this)))\n\n          Seqable\n          (seq [this] (.seq ^Seqable (.-data this)))\n\n          java.util.Set\n          (toArray [this] (.toArray (.-data this)))\n          (^\"[Ljava.lang.Object;\" toArray [this ^\"[Ljava.lang.Object;\" a]\n           (.toArray ^java.util.Set (.-data this) a))\n          (add [this o] (throw (UnsupportedOperationException.)))\n          (remove [this o] (throw (UnsupportedOperationException.)))\n          (addAll [this c] (throw (UnsupportedOperationException.)))\n          (clear [this] (throw (UnsupportedOperationException.)))\n          (retainAll [this c] (throw (UnsupportedOperationException.)))\n          (removeAll [this c] (throw (UnsupportedOperationException.)))\n          (containsAll [this c] (.containsAll ^java.util.Set (.-data this) c))\n          (size [this] (.size ^java.util.Set (.-data this)))\n          (isEmpty [this] (.isEmpty ^java.util.Set (.-data this)))\n          (iterator [this] (.iterator ^java.util.Set (.-data this)))\n\n          IFn\n          (invoke [this arg1]\n            (.invoke ^IFn (.-data this) arg1))\n\n          IObj\n          (withMeta [this meta]\n            (Set. (.withMeta ^IObj (.-data this) meta) (.-vclock this) (.-birth-dots this)))\n\n          IMeta\n          (meta [this]\n            (.meta ^IMeta (.-data this))))\n   :cljs (deftype Set [data vclock birth-dots]\n           ICounted\n           (-count [this] (-count (.-data this)))\n\n           IEmptyableCollection\n           (-empty [this] (orswot-empty this))\n\n           ICollection\n           (-conj [this o] (orswot-conj this o))\n\n           ISet\n           (-disjoin [this o] (orswot-disj this o))\n\n           IEquiv\n           (-equiv [this other]\n             (-equiv (.-data this) other))\n\n           ILookup\n           (-lookup [this o]\n             (-lookup (.-data this) o))\n           (-lookup [this o not-found]\n             (-lookup (.-data this) o not-found))\n\n           IPrintWithWriter\n           (-pr-writer [o writer opts]\n             (-write writer \"#schism/set [\")\n             (-write writer (pr-str (.-data o)))\n             (-write writer \", \")\n             (-write writer (pr-str (.-vclock o)))\n             (-write writer \", \")\n             (-write writer (pr-str (.-birth-dots o)))\n             (-write writer \"]\"))\n\n           IHash\n           (-hash [this] (-hash (.-data this)))\n\n           IFn\n           (-invoke [this o] (-invoke (.-data this) o))\n\n           ISeqable\n           (-seq [this] (-seq (.-data this)))\n\n           Object\n           (toString [this] (.toString (.-data this)))\n\n           IMeta\n           (-meta [this]\n             (-meta (.-data this)))\n\n           IWithMeta\n           (-with-meta [this meta]\n             (Set. (-with-meta (.-data this)\n                               meta)\n                   (.-vclock this) (.-birth-dots this)))))\n\n(defn orswot-conj\n  [^Set orswot o]\n  (vc/update-clock now orswot\n                   (Set. (conj (.-data orswot) o)\n                         (.-vclock orswot)\n                         (assoc (.-birth-dots orswot) o [node/*current-node* now]))))\n\n(defn orswot-empty\n  [^Set orswot]\n  (vc/update-clock _ orswot\n                   (Set. (hash-set)\n                         (hash-map)\n                         (hash-map))))\n\n(defn orswot-disj\n  [^Set orswot o]\n  (vc/update-clock _ orswot\n                   (Set. (disj (.-data orswot) o)\n                         (.-vclock orswot)\n                         (dissoc (.-birth-dots orswot) o))))\n\n(defn- elemental-data\n  [^Set s]\n  {:vector-clock (.-vclock s)\n   :elements (into []\n                   (for [datum (.-data s)]\n                     (let [dot (get (.-birth-dots s) datum)]\n                       {:data datum\n                        :author-node (first dot)\n                        :record-time (last dot)})))})\n\n(extend-type Set\n  proto/Vclocked\n  (get-clock [this] (.-vclock this))\n  (with-clock [this new-clock] (Set. (.-data this)\n                                     new-clock\n                                     (.-birth-dots this)))\n\n  proto/Convergent\n  (synchronize [this ^Set other]\n    (let [own-meta (-> this .-data meta)\n          own-data (elemental-data this)\n          other-data (elemental-data other)\n          retain (filter (ic/common-elements own-data other-data)\n                         (:elements own-data))\n          completed-elements (->> (apply ic/retain-elements\n                                         (ic/distinct-data own-data other-data))\n                                  (concat retain)\n                                  (sort-by :record-time))\n          completed-data (into #{} (map :data completed-elements))\n          completed-birth-dots (->>  completed-elements\n                                     (map (fn [{:keys [data author-node record-time]}]\n                                            [data [author-node record-time]]))\n                                     (into {}))\n          completed-vclock (ic/merged-clock completed-elements own-data other-data)]\n      (vc/update-clock _ this\n                       (Set. (with-meta completed-data\n                               own-meta)\n                             completed-vclock\n                             completed-birth-dots)))))\n\n#?(:clj (defmethod print-method Set\n          [^Set s ^Writer writer]\n          (.write writer \"#schism/set [\")\n          (.write writer (pr-str (.-data s)))\n          (.write writer \", \")\n          (.write writer (pr-str (.-vclock s)))\n          (.write writer \", \")\n          (.write writer (pr-str (.-birth-dots s)))\n          (.write writer \"]\")))\n\n(defn read-edn-set\n  [read-object]\n  (let [[data vclock birth-dots] read-object]\n    (Set. data vclock birth-dots)))\n\n#?(:cljs (cljs.reader/register-tag-parser! 'schism/set read-edn-set))\n\n(defn new-set\n  ([] (Set. (hash-set)\n            (hash-map)\n            (hash-map)))\n  ([& args] (vc/update-clock now nil\n                             (Set. (apply hash-set args)\n                                   (hash-map)\n                                   (apply hash-map\n                                          (mapcat (fn [o]\n                                                    [o [node/*current-node* now]])\n                                                  args))))))\n"
  },
  {
    "path": "src/schism/impl/types/vector.cljc",
    "content": "(ns schism.impl.types.vector\n  \"Definition and support for Schism's Convergent vector type. The\n  convergent vector is a timestamped log of entries with a vector\n  clock & insertion index. Convergence places entries into the\n  resultant vector in insertion order, with insertions occurring by\n  replaying insertions operations in order. The vector clock conveys\n  that an item has been removed from the vector on another node.\"\n  (:require [schism.impl.protocols :as proto]\n            [schism.impl.vector-clock :as vc]\n            [schism.impl.core :as ic]\n            [schism.node :as node]\n            [clojure.set :as set]\n            #?(:cljs [cljs.reader :as reader]))\n  #?(:cljs (:require-macros [schism.impl.vector-clock :as vc]))\n  #?(:clj (:import (clojure.lang IPersistentCollection IPersistentStack IPersistentVector Reversible IReduce IKVReduce Indexed Associative Counted IHashEq Seqable IObj IMeta IFn ILookup)\n                   (java.io Writer)\n                   (java.util Date Collection)\n                   (java.lang Object Long))))\n\n;; A CLJ & CLJS implementation of a convergent vector\n\n;; Each vector maintains its own vector clock, and insertion times and\n;; positions for each element of the vector. Vector entries and\n;; insertions are correlated positionally (as the vector may contain\n;; the same item multiple times.) Insertion times and indices dictate\n;; ordering; elements inserted at the tail of the vector are recorded\n;; as being inserted with index -1. The vector clock determines if an\n;; entry has been removed.\n\n(declare cvector-conj cvector-pop cvector-empty cvector-assoc)\n\n#?(:clj (deftype ConvergentVector [data vclock insertions]\n          Counted\n          (count [this] (.count ^Counted (.-data this)))\n\n          IPersistentCollection\n          (cons [this o] (cvector-conj this o))\n          (empty [this] (cvector-empty this))\n          (equiv [this other] (.equiv ^IPersistentCollection (.-data this) other))\n\n          Object\n          (equals [this o]\n            (.equals (.-data this) o))\n          (hashCode [this]\n            (.hashCode (.-data this)))\n          (toString [this]\n            (.toString (.-data this)))\n\n          IHashEq\n          (hasheq [this]\n            (.hasheq ^IHashEq (.-data this)))\n\n          Seqable\n          (seq [this]\n            (seq ^Seqable (.-data this)))\n\n          java.util.List\n          (add [this o] (.add ^java.util.List (.-data this) o))\n          (add [this index o] (.add ^java.util.List (.-data this) index o))\n          (addAll [this c] (.addAll ^java.util.List (.-data this) c))\n          (clear [this] (.clear ^java.util.List (.-data this)))\n          (contains [this o] (.contains ^java.util.List (.-data this) o))\n          (containsAll [this c] (.containsAll ^java.util.List (.-data this) c))\n          (get [this i] (.get ^java.util.List (.-data this) i))\n          (indexOf [this o] (.indexOf ^java.util.List (.-data this) o))\n          (isEmpty [this] (.isEmpty ^java.util.List (.-data this)))\n          (iterator [this] (.iterator ^java.util.List (.-data this)))\n          (lastIndexOf [this o] (.lastIndexOf ^java.util.List (.-data this) o))\n          (listIterator [this] (.listIterator ^java.util.List (.-data this)))\n          (^Object remove [this ^int i] (.remove ^java.util.List (.-data this) i))\n          (^boolean remove [this ^Object o] (.remove ^java.util.List (.-data this) o))\n          (removeAll [this c] (.removeAll ^java.util.List (.-data this) c))\n          (replaceAll [this op] (.replaceAll ^java.util.List (.-data this) op))\n          (retainAll [this c] (.retainAll ^java.util.List (.-data this) c))\n          (set [this i e] (.set ^java.util.List (.-data this) i e))\n          (size [this] (.size ^java.util.List (.-data this)))\n          (sort [this c] (.sort ^java.util.List (.-data this) c))\n          (spliterator [this] (.spliterator ^java.util.List (.-data this)))\n          (subList [this i j] (.subList ^java.util.List (.-data this) i j))\n          (toArray [this] (.toArray ^java.util.List (.-data this)))\n          (^\"[Ljava.lang.Object;\" toArray [this ^\"[Ljava.lang.Object;\" a]\n           (.toArray ^java.util.List (.-data this) a))\n\n\n          IObj\n          (withMeta [this meta]\n            (ConvergentVector. (with-meta ^IObj (.-data this)\n                                 meta)\n                               (.-vclock this)\n                               (.-insertions this)))\n\n          IMeta\n          (meta [this]\n            (.meta ^IMeta (.-data this)))\n\n          IReduce\n          (reduce [this f]\n            (.reduce ^IReduce (.-data this) f))\n\n          IKVReduce\n          (kvreduce [this f init]\n            (.kvreduce ^IKVReduce (.-data this) f init))\n\n          IPersistentStack\n          (peek [this] (.peek ^IPersistentStack (.-data this)))\n          (pop [this]\n            (cvector-pop this))\n\n          IPersistentVector\n          (assocN [this i v]\n            (cvector-assoc this i v))\n\n          ILookup\n          (valAt [this k]\n            (.valAt (.-data this) k))\n          (valAt [this k not-found]\n            (.valAt (.-data this) k not-found))\n\n          Associative\n          (containsKey [this k]\n            (.containsKey ^Associative (.-data this) k))\n          (entryAt [this k]\n            (.entryAt ^Associative (.-data this) k))\n          (assoc [this k v]\n            (cvector-assoc this k v))\n\n          Indexed\n          (nth [this i]\n            (.indexed ^Indexed (.-data this) i))\n          (nth [this i not-found]\n            (.indexed ^Indexed (.-data this) i not-found))\n\n          IFn\n          (invoke [this k]\n            (.invoke ^IFn (.-data this) k))\n          (invoke [this k not-found]\n            (.invoke ^IFn (.-data this) k not-found)))\n   :cljs (deftype ConvergentVector [data vclock insertions]\n           ICounted\n           (-count [this] (-count (.-data this)))\n\n           IEmptyableCollection\n           (-empty [this] (cvector-empty this))\n\n           ICollection\n           (-conj [this o] (cvector-conj this o))\n\n           IEquiv\n           (-equiv [this other]\n             (-equiv (.-data this) other))\n\n           IPrintWithWriter\n           (-pr-writer [o writer opts]\n             (-write writer \"#schism/vector [\")\n             (-write writer (pr-str (.-data o)))\n             (-write writer \", \")\n             (-write writer (pr-str (.-vclock o)))\n             (-write writer \", \")\n             (-write writer (pr-str (.-insertions o)))\n             (-write writer \"]\"))\n\n           IHash\n           (-hash [this] (-hash (.-data this)))\n\n           ISeqable\n           (-seq [this] (-seq (.-data this)))\n\n           Object\n           (toString [this] (.toString (.-data this)))\n\n           IMeta\n           (-meta [this]\n             (-meta (.-data this)))\n\n           IWithMeta\n           (-with-meta [this meta]\n             (ConvergentVector. (-with-meta (.-data this)\n                                            meta)\n                                (.-vclock this)\n                                (.-insertions this)))\n\n           IFn\n           (-invoke [this k]\n             (-invoke (.-data this) k))\n           (-invoke [this k not-found]\n             (-invoke (.-data this) k not-found))\n\n           IIndexed\n           (-nth [this n]\n             (-nth (.-data this) n))\n           (-nth [this n not-found]\n             (-nth (.-data this) n not-found))\n\n           ILookup\n           (-lookup [this k]\n             (-lookup (.-data this) k))\n           (-lookup [this k not-found]\n             (-lookup (.-data this) k not-found))\n\n           IAssociative\n           (-contains-key? [this k]\n             (-contains-key? (.-data this) k))\n           (-assoc [this k v]\n             (cvector-assoc this k v))\n\n           IFind\n           (-find [this k]\n             (-find (.-data this) k))\n\n           IStack\n           (-peek [this]\n             (-peek (.-data this)))\n           (-pop [this]\n             (cvector-pop this))\n\n           IVector\n           (-assoc-n [this n v]\n             (cvector-assoc this n v))\n\n           IReduce\n           (-reduce [this f]\n             (-reduce (.-data this) f))\n           (-reduce [this f start]\n             (-reduce (.-data this) f start))\n\n           IKVReduce\n           (-kv-reduce [this f init]\n             (-kv-reduce (.-data this) f init))))\n\n(defn cvector-conj [^ConvergentVector cvector o]\n  (vc/update-clock now cvector\n                   (ConvergentVector. (conj (.-data cvector) o)\n                                      (.-vclock cvector)\n                                      (conj (.-insertions cvector) [node/*current-node* -1 now]))))\n\n(defn cvector-empty [^ConvergentVector cvector]\n  (vc/update-clock _ cvector\n                   (ConvergentVector. (vector)\n                                      (hash-map)\n                                      (vector))))\n\n(defn cvector-pop [^ConvergentVector cvector]\n  (vc/update-clock _ cvector\n                   (ConvergentVector. (pop (.-data cvector))\n                                      (.-vclock cvector)\n                                      (pop (.-insertions cvector)))))\n\n(defn cvector-assoc [^ConvergentVector cvector k v]\n  (vc/update-clock now cvector\n                   (ConvergentVector. (assoc (.-data cvector) k v)\n                                      (.-vclock cvector)\n                                      (assoc (.-insertions cvector) k [node/*current-node* k now]))))\n\n(defn- elemental-data\n  [^ConvergentVector v]\n  {:vector-clock (.-vclock v)\n   :elements (into []\n                   (for [[element [author-node insert-index record-time]]\n                         (map vector (.-data v) (.-insertions v))]\n                     {:data {:element element\n                             :insert-index insert-index}\n                      :author-node author-node\n                      :record-time record-time}))})\n\n(extend-type ConvergentVector\n  proto/Vclocked\n  (get-clock [this] (.-vclock this))\n  (with-clock [this new-clock] (ConvergentVector. (.-data this)\n                                                  new-clock\n                                                  (.-insertions this)))\n\n  proto/Convergent\n  (synchronize [this ^ConvergentVector other]\n    (let [own-meta (-> this .-data meta)\n          own-data (elemental-data this)\n          other-data (elemental-data other)\n          retain (filter (ic/common-elements own-data other-data)\n                         (:elements own-data))\n          completed-elements (->> retain\n                                  (concat (apply ic/retain-elements\n                                                 (ic/distinct-data own-data other-data)))\n                                  (sort-by (fn [{:keys [author-node data record-time]}]\n                                             (let [{:keys [insert-index]} data]\n                                               [(if (= -1 insert-index)\n                                                  ic/tail-insertion-sort-value\n                                                  insert-index) record-time]))))\n          ;; Given the potential for insertion at arbitrary indexes,\n          ;; trying to find a common contiguous chunk is less fruitful\n          ;; than taking our [element, node, index, timestamp] tuples\n          ;; and treating them as discrete insertion\n          ;; instructions. Removal is still indicated by vector clock,\n          ;; AND it is reasonable to expect much of the vector to be\n          ;; shared, so it's important to get to the right set of such\n          ;; instructions. As time and index dictate the overall state\n          ;; of convergence, it is not important to preserve ordering\n          ;; through convergence, which affords using set logic to\n          ;; find the common insertions.\n          completed-data (reduce (fn [m element]\n                                   (let [{:keys [element insert-index]} (:data element)]\n                                     (ic/assoc-n-with-tail-support m insert-index element)))\n                                 []\n                                 completed-elements)\n          completed-insertions (reduce (fn [m {:keys [author-node record-time]\n                                               {:keys [insert-index]} :data}]\n                                         (ic/assoc-n-with-tail-support m insert-index\n                                                                       [author-node insert-index record-time]))\n                                       []\n                                       completed-elements)\n          completed-vclock (ic/merged-clock completed-elements own-data other-data)]\n      (vc/update-clock _ this\n                       (ConvergentVector. (with-meta completed-data own-meta)\n                                          completed-vclock\n                                          completed-insertions)))))\n\n#?(:clj (defmethod print-method ConvergentVector\n          [^ConvergentVector v ^Writer writer]\n          (.write writer \"#schism/vector [\")\n          (.write writer (pr-str (.-data v)))\n          (.write writer \", \")\n          (.write writer (pr-str (.-vclock v)))\n          (.write writer \", \")\n          (.write writer (pr-str (.-insertions v)))\n          (.write writer \"]\")))\n\n(defn read-edn-vector\n  [read-object]\n  (let [[data vclock insertions] read-object]\n    (ConvergentVector. data vclock insertions)))\n\n#?(:cljs (cljs.reader/register-tag-parser! 'schism/vector read-edn-vector))\n\n(defn new-vector\n  ([] (ConvergentVector. (vector)\n                         (hash-map)\n                         (vector)))\n  ([& args] (vc/update-clock now nil\n                             (ConvergentVector. (apply vector args)\n                                                (hash-map)\n                                                (apply vector (for [i (range (count args))]\n                                                                [node/*current-node* i now]))))))\n"
  },
  {
    "path": "src/schism/impl/vector_clock.cljc",
    "content": "(ns schism.impl.vector-clock\n  \"Utility functions for working with the vector clock of a value that\n  participates in the Vclocked protocol.\"\n  (:require [schism.impl.protocols :as sp]\n            [schism.impl.core :as ic]\n            [schism.node :as node])\n  #?(:clj (:import (java.util Date))))\n\n(defmacro update-clock\n  \"Binds the current time to `binding`, executes body, then updates\n  the body's return value which participates in\n  schism.impl.protocols/Vclocked, so that the vector clock contains the\n  same time bound to `binding` for the current node.\"\n  [binding clocked & body]\n  `(let [now# (ic/to-date\n               (max (ic/to-millis (ic/now))\n                    (inc (apply max 0 (map ic/to-millis (vals (sp/get-clock ~clocked)))))))\n         ~binding now#\n         ret# (do ~@body)\n         ret-clock# (sp/get-clock ret#)]\n     (sp/with-clock ret# (assoc ret-clock# node/*current-node* now#))))\n"
  },
  {
    "path": "src/schism/node.cljc",
    "content": "(ns schism.node\n  #?(:clj (:import (java.util UUID))))\n\n(def ^:dynamic *current-node* nil)\n\n(defn initialize-node!\n  \"Initialize `schism.node/*current-node*` to the passed in value,\n  or a new random UUID. While any serializable value suffices as the\n  current node, it should be unique within the cluster; a repeated\n  node value may result in incorrect behaviors during convergence.\"\n  ([] (initialize-node!\n       #?(:clj (UUID/randomUUID)\n          :cljs (random-uuid))))\n  ([id] #?(:clj (alter-var-root #'*current-node* (constantly id))\n           :cljs (set! *current-node* id))))\n\n(defmacro with-node\n  \"Override the value of `schism.node/*current-node*` for the scope of\n  `body`.\"\n  [id & body]\n  `(binding [*current-node* ~id]\n     ~@body))\n"
  },
  {
    "path": "test/schism/core_test.cljc",
    "content": "(ns schism.core-test\n  (:require [schism.core :as schism :include-macros true]\n            #?(:clj [clojure.test :refer [deftest testing is]]\n               :cljs [cljs.test :refer [deftest testing is]])\n            [clojure.test.check.generators :as gen]\n            [clojure.test.check.properties :as prop :include-macros true]\n            [clojure.test.check.clojure-test #?(:clj :refer\n                                                :cljs :refer-macros) [defspec]]))\n\n#?(:cljs\n   (deftest collections-with-NaN\n     (is (not= (hash-map js/NaN [])\n               (hash-map js/NaN []))\n         \"Because NaN cannot be compared for equality, two maps with NaN cannot be equal.\")\n     (is (not= (list js/NaN)\n               (list js/NaN))\n         \"Because NaN cannot be compared for equality, two lists with NaN cannot be equal.\")\n     (is (not= (hash-set js/NaN)\n               (hash-set js/NaN))\n         \"Because NaN cannot be compared for equality, two hash-sets with NaN cannot be equal.\")\n     (is (not= (vector js/NaN)\n               (vector js/NaN))\n         \"Because NaN cannot be compared for equality, two vectors with NaN cannot be equal.\")))\n\n(def collection-any\n  \"CLJS any will sometimes return NaN, but property tests using\n  equality of collections cannot pass with NaN as an element (see\n  above), so explicitly filter out NaN\"\n  #?(:cljs (gen/such-that #(not (js/Number.isNaN %)) gen/any)\n     :clj gen/any))\n\n\n\n(defspec convergent-set-is-equivalent-to-hash-set\n  50\n  (prop/for-all [v (gen/vector collection-any)]\n                (= (apply schism/convergent-set v)\n                   (apply hash-set v))))\n\n(defspec convergent-map-is-equivalent-to-hash-map\n  50\n  (prop/for-all [entries (gen/fmap (comp (partial mapcat identity) seq) (gen/map collection-any collection-any))]\n                (= (apply schism/convergent-map entries)\n                   (apply hash-map entries))))\n\n(defspec nested-map-is-equivalent-to-hash-map\n  50\n  (prop/for-all [entries (gen/fmap (comp (partial mapcat identity) seq) (gen/map collection-any collection-any))]\n                (= (apply schism/nested-map entries)\n                   (apply hash-map entries))))\n\n(defspec convergent-vector-is-equivalent-to-vector\n  50\n  (prop/for-all [v (gen/vector collection-any)]\n                (= (apply schism/convergent-vector v)\n                   (apply vector v))))\n\n(defspec nested-vector-is-equivalent-to-vector\n  50\n  (prop/for-all [v (gen/vector collection-any)]\n                (= (apply schism/nested-vector v)\n                   (apply vector v))))\n\n(defspec convergent-list-is-equivalent-to-list\n  50\n  (prop/for-all [v (gen/list collection-any)]\n                (= (apply schism/convergent-list v)\n                   (apply list v))))\n\n(defspec conj-equivalence-for-sets\n  50\n  (prop/for-all [v (gen/vector collection-any)\n                 e collection-any]\n                (= (conj (apply schism/convergent-set v) e)\n                   (conj (apply hash-set v) e))))\n\n(defspec conj-equivalence-for-maps\n  30\n  (prop/for-all [entries (gen/fmap (comp (partial mapcat identity) seq) (gen/map collection-any collection-any))\n                 e (gen/tuple collection-any collection-any)]\n                (= (conj (apply schism/convergent-map entries) e)\n                   (conj (apply hash-map entries) e))))\n\n(defspec conj-equivalence-for-nested-maps\n  30\n  (prop/for-all [entries (gen/fmap (comp (partial mapcat identity) seq) (gen/map collection-any collection-any))\n                 e (gen/tuple collection-any collection-any)]\n                (= (conj (apply schism/nested-map entries) e)\n                   (conj (apply hash-map entries) e))))\n\n\n(defspec conj-equivalence-for-vectors\n  30\n  (prop/for-all [v (gen/vector collection-any)\n                 e collection-any]\n                (= (conj (apply schism/convergent-vector v) e)\n                   (conj (apply vector v) e))))\n\n(defspec conj-equivalence-for-nested-vectors\n  30\n  (prop/for-all [v (gen/vector collection-any)\n                 e collection-any]\n                (= (conj (apply schism/nested-vector v) e)\n                   (conj (apply vector v) e))))\n\n(defspec conj-equivalence-for-lists\n  30\n  (prop/for-all [v (gen/vector collection-any)\n                 e collection-any]\n                (= (conj (apply schism/convergent-list v) e)\n                   (conj (apply list v) e))))\n\n(defspec rest-equivalence-for-lists\n  30\n  (prop/for-all [v (gen/vector collection-any)]\n                (= (rest (apply schism/convergent-list v))\n                   (rest (apply list v)))))\n\n(defspec assoc-equivalence-for-maps\n  30\n  (prop/for-all [entries (gen/fmap (comp (partial mapcat identity) seq) (gen/map collection-any collection-any))\n                 k collection-any\n                 v collection-any]\n                (= (assoc (apply schism/convergent-map entries) k v)\n                   (assoc (apply hash-map entries) k v))))\n\n(defspec assoc-equivalence-for-nested-maps\n  30\n  (prop/for-all [entries (gen/fmap (comp (partial mapcat identity) seq) (gen/map collection-any collection-any))\n                 k collection-any\n                 v collection-any]\n                (= (assoc (apply schism/nested-map entries) k v)\n                   (assoc (apply hash-map entries) k v))))\n\n(defspec assoc-equivalence-for-vectors\n  30\n  (prop/for-all [v (gen/such-that #(< 0 (count %))\n                                  (gen/vector collection-any))\n                 e collection-any]\n                (gen/let [index (gen/choose 0 (dec (count v)))]\n                 (= (assoc (apply schism/convergent-vector v) index e)\n                    (assoc (apply vector v) index e)))))\n\n(defspec assoc-equivalence-for-nested-vectors\n  30\n  (prop/for-all [v (gen/such-that #(< 0 (count %))\n                                  (gen/vector collection-any))\n                 e collection-any]\n                (gen/let [index (gen/choose 0 (dec (count v)))]\n                 (= (assoc (apply schism/nested-vector v) index e)\n                    (assoc (apply vector v) index e)))))\n\n(defspec pop-equivalence-for-vectors\n  30\n  (prop/for-all [v (gen/such-that #(< 0 (count %))\n                                  (gen/vector collection-any))]\n                (= (pop (apply schism/convergent-vector v))\n                   (pop (apply vector v)))))\n\n(defspec pop-equivalence-for-nested-vectors\n  30\n  (prop/for-all [v (gen/such-that #(< 0 (count %))\n                                  (gen/vector collection-any))]\n                (= (pop (apply schism/nested-vector v))\n                   (pop (apply vector v)))))\n\n(defspec dissoc-present-key-for-maps\n  30\n  (prop/for-all [entries (gen/fmap (comp (partial mapcat identity) seq)\n                                   (gen/map collection-any collection-any {:min-elements 1}))]\n                (gen/let [key (gen/elements (keys (apply hash-map entries)))]\n                  (= (dissoc (apply schism/convergent-map entries) key)\n                     (dissoc (apply hash-map entries) key)))))\n\n(defspec dissoc-present-key-for-nested-maps\n  30\n  (prop/for-all [entries (gen/fmap (comp (partial mapcat identity) seq)\n                                   (gen/map collection-any collection-any {:min-elements 1}))]\n                (gen/let [key (gen/elements (keys (apply hash-map entries)))]\n                  (= (dissoc (apply schism/nested-map entries) key)\n                     (dissoc (apply hash-map entries) key)))))\n\n(defspec dissoc-random-value-for-maps\n  30\n  (prop/for-all [entries (gen/fmap (comp (partial mapcat identity) seq)\n                                   (gen/map collection-any collection-any {:min-elements 1}))\n                 key gen/any]\n                (= (dissoc (apply schism/convergent-map entries) key)\n                   (dissoc (apply hash-map entries) key))))\n\n(defspec dissoc-random-value-for-nested-maps\n  30\n  (prop/for-all [entries (gen/fmap (comp (partial mapcat identity) seq)\n                                   (gen/map collection-any collection-any {:min-elements 1}))\n                 key gen/any]\n                (= (dissoc (apply schism/nested-map entries) key)\n                   (dissoc (apply hash-map entries) key))))\n\n\n(defspec disj-included-element-for-sets\n  5\n  (prop/for-all [s (gen/set collection-any {:min-elements 1})]\n                (gen/let [e (gen/elements (apply hash-set s))]\n                  (= (disj (apply schism/convergent-set s) e)\n                     (disj (apply hash-set s) e)))))\n\n(defspec disj-random-element-for-sets\n  5\n  (prop/for-all [s (gen/set collection-any {:min-elements 1})\n                 e collection-any]\n                (= (disj (apply schism/convergent-set s) e)\n                   (disj (apply hash-set s) e))))\n\n(defspec converge-after-ops-equivalent-for-sets\n  50\n  (prop/for-all [s (gen/set collection-any {:min-elements 3})\n                 ops (gen/let [operants (gen/list collection-any)\n                               operands (gen/vector (gen/elements [conj disj]) (count operants))]\n                       (map vector operands operants))]\n                (let [basis-cset (schism/with-node :start\n                                   (apply schism/convergent-set s))\n                      vanilla-result (reduce (fn [memo [f operant]]\n                                               (f memo operant))\n                                             s\n                                             ops)\n                      schism-result (schism/with-node :end\n                                      (reduce (fn [memo [f operant]]\n                                                (f memo operant))\n                                              basis-cset\n                                              ops))\n                      converge-result (schism/with-node :start\n                                        (schism/converge basis-cset schism-result))]\n                  (= vanilla-result schism-result converge-result))))\n"
  },
  {
    "path": "test/schism/impl/types/list_test.cljc",
    "content": "(ns schism.impl.types.list-test\n  (:require #?(:clj [clojure.test :refer [deftest testing is]]\n               :cljs [cljs.test :refer [deftest testing is]])\n            #?(:cljs [cljs.reader :as reader])\n            [schism.impl.types.list :as slist]\n            [schism.node :as node]\n            [schism.impl.protocols :as proto])\n  #?(:clj (:import schism.impl.types.list.ConvergentList)))\n\n\n(deftest basic-IPC-ops\n  (testing \"Equiv for lists\"\n    (is (= (slist/new-list) '()))\n    (is (= (slist/new-list :a true) '(:a true))))\n  (testing \"Empty for lists\"\n    (is (= (empty (slist/new-list :a true)) (empty '(:a true)))))\n  (testing \"Count for lists\"\n    (is (= (count (slist/new-list :a true)) (count '(:a true)))))\n  (testing \"Conj for lists\"\n    (is (= (conj (slist/new-list) [:a true]) (conj '() [:a true])))\n    (is (= (conj (slist/new-list :a true) [:a false]) (conj '(:a true) [:a false])))))\n\n(deftest basic-seq-ops\n  (testing \"conj\"\n    (is (= (conj (slist/new-list :a true) :a) (conj '(:a true) :a))))\n  (testing \"rest\"\n    (is (= (rest (slist/new-list :a true)) (rest '(:a true))))))\n\n(deftest converge-test\n  (testing \"Converge after concurrent additions on another node.\"\n    (node/initialize-node! :converge-test-origin)\n    ;; Can only rely on millisecond time scales, so sleep 1 second\n    ;; between ops so that there's some non-zero passage of time\n    (let [transfer (-> (slist/new-list true :a)\n                       (conj [:b 3]))\n          other (node/with-node :converge-test-other-node\n                  (conj transfer [:c :d]))\n          result (proto/synchronize transfer other)]\n      (is (= result '([:c :d] [:b 3] true :a)))))\n  (testing \"Rest on another node mirrored locally after converge.\"\n    (node/initialize-node! :converge-test-origin)\n    (let [transfer (slist/new-list :a true :b 3 :c :d)\n          other (node/with-node :converge-test-other-node\n                  (rest transfer))\n          result (proto/synchronize transfer other)]\n      (is (= other '(true :b 3 :c :d)))\n      (is (= result '(true :b 3 :c :d))))))\n\n(deftest seqable-test\n  (testing \"Can turn a CLIST into a seq\"\n    (is (= (seq (slist/new-list :a true :b 3 :c :d))\n           (seq (list :a true :b 3 :c :d))))))\n\n(deftest string-test\n  (testing \"Prints to console readably, even though edn is verbose\"\n    (is (= (str (slist/new-list :a true :b 3 :c :d))\n           (str (list :a true :b 3 :c :d))))))\n\n(deftest serialization-test\n  (testing \"Round trip serialization generates the same structure.\"\n    (let [^ConvergentList origin (-> (slist/new-list :a true :b 3 :c :d)\n                                     (conj [:d :quux])\n                                     rest)\n          ^ConvergentList round-tripped (-> origin\n                                            pr-str\n                                            #?(:clj read-string\n                                               :cljs reader/read-string))]\n      (is (= (.-data origin) (.-data round-tripped)))\n      (is (= (.-vclock origin) (.-vclock round-tripped)))\n      (is (= (.-insertions origin) (.-insertions round-tripped))))))\n\n(deftest hashing-test\n  (testing \"Hashes to the same value as an equivalent list\"\n    (is (= (hash (into (slist/new-list) [[:a true] [:b 3] [:c :d]]))\n           (hash (into '() [[:a true] [:b 3] [:c :d]]))))))\n\n(deftest meta-test\n  (testing \"Metadata on CLISTs\"\n    (is (= (meta (with-meta (slist/new-list) {:test :data}))\n           {:test :data}))))\n"
  },
  {
    "path": "test/schism/impl/types/map_test.cljc",
    "content": "(ns schism.impl.types.map-test\n  (:require #?(:clj [clojure.test :refer [deftest testing is]]\n               :cljs [cljs.test :refer [deftest testing is]])\n            #?(:cljs [cljs.reader :as reader])\n            [schism.impl.types.map :as smap]\n            [schism.node :as node]\n            [schism.impl.protocols :as proto])\n  #?(:clj (:import schism.impl.types.map.Map)))\n\n(deftest basic-IPC-ops\n  (testing \"Equiv for maps\"\n    (is (= (smap/new-map) {}))\n    (is (= (smap/new-map :a true) {:a true})))\n  (testing \"Empty for maps\"\n    (is (= (empty (smap/new-map :a true)) (empty {:a true}))))\n  (testing \"Count for maps\"\n    (is (= (count (smap/new-map :a true)) (count {:a true}))))\n  (testing \"Conj for maps\"\n    (is (= (conj (smap/new-map) [:a true]) (conj {} [:a true])))\n    (is (= (conj (smap/new-map :a true) [:a false]) (conj {:a true} [:a false])))))\n\n(deftest basic-IPS-ops\n  (testing \"dissoc\"\n    (is (= (dissoc (smap/new-map :a true) :a) (dissoc {:a true} :a))))\n  (testing \"contains\"\n    (is (= (contains? (smap/new-map :a true) :a) (contains? {:a true} :a))))\n  (testing \"get\"\n    (is (= (get (smap/new-map :a true) :a) (get {:a true} :a)))))\n\n(deftest converge-test\n  (testing \"Converge after concurrent additions on another node.\"\n    (node/initialize-node! :converge-test-origin)\n    ;; Can only rely on millisecond time scales, so sleep 1 second\n    ;; between ops so that there's some non-zero passage of time\n    (let [transfer (-> (smap/new-map :a true)\n                       (conj [:b 3]))\n          other (node/with-node :converge-test-other-node\n                  (conj transfer [:c :d]))\n          result (proto/synchronize transfer other)]\n      (is (= result {:a true :b 3 :c :d}))\n      (is (= {:a true :b 3 :c :d} (.-data result)))\n      (doseq [[k v] (.-vclock result)]\n        (is (#{:converge-test-origin :converge-test-other-node} k))\n        (is (instance? #?(:clj java.util.Date\n                          :cljs js/Date) v)))\n      (is (= #{:converge-test-origin :converge-test-other-node} (set (keys (.-vclock result)))))\n      (is (= (set (keys (.-data result))) (set (keys (.-birth-dots result)))))\n      (doseq [[key [node time]] (.-birth-dots result)]\n        (is (#{:a :b :c} key))\n        (is (#{:converge-test-origin :converge-test-other-node} node))\n        (is (instance? #?(:clj java.util.Date\n                          :cljs js/Date) time)))))\n  (testing \"Dissoc on another node mirrored locally after converge.\"\n    (node/initialize-node! :converge-test-origin)\n    (let [transfer (smap/new-map :a true :b 3 :c :d)\n          other (node/with-node :converge-test-other-node\n                  (dissoc transfer :c))\n          result (proto/synchronize transfer other)]\n      (is (= other {:a true :b 3}))\n      (is (= result {:a true :b 3})))))\n\n(deftest seqable-test\n  (testing \"Can turn an ORMWOT into a seq\"\n    (is (= (seq (smap/new-map :a true :b 3 :c :d))\n           (seq (hash-map :a true :b 3 :c :d))))))\n\n(deftest ifn-test\n  (testing \"Can invoke an ORMWOT\"\n    (is (= ((smap/new-map :a true :b 3 :c :d) :c)\n           ({:a true :b 3 :c :d} :c)))))\n\n(deftest string-test\n  (testing \"Prints to console readably, even though edn is verbose\"\n    (is (= (str (smap/new-map :a true :b 3 :c :d))\n           (str (hash-map :a true :b 3 :c :d))))))\n\n(deftest serialization-test\n  (testing \"Round trip serialization generates the same structure.\"\n    (let [^Map origin (-> (smap/new-map :a true :b 3 :c :d)\n                          (conj [:d :quux])\n                          (dissoc :c))\n          ^Map round-tripped (-> origin\n                                 pr-str\n                                 #?(:clj read-string\n                                    :cljs reader/read-string))]\n      (is (= (.-data origin) (.-data round-tripped)))\n      (is (= (.-vclock origin) (.-vclock round-tripped)))\n      (is (= (.-birth-dots origin) (.-birth-dots round-tripped))))))\n\n(deftest hashing-test\n  (testing \"Hashes to the same value as an equivalent hash-map\"\n    (is (= (hash (into (smap/new-map) [[:a true] [:b 3] [:c :d]]))\n           (hash (into {} [[:a true] [:b 3] [:c :d]]))))))\n\n(deftest meta-test\n  (testing \"Metadata on ORMWOTs\"\n    (is (= (meta (with-meta (smap/new-map) {:test :data}))\n           {:test :data}))))\n"
  },
  {
    "path": "test/schism/impl/types/nested_map_test.cljc",
    "content": "(ns schism.impl.types.nested-map-test\n  (:require #?(:clj [clojure.test :refer [deftest testing is]]\n               :cljs [cljs.test :refer [deftest testing is]])\n            #?(:cljs [cljs.reader :as reader])\n            [schism.impl.types.nested-map :as nmap]\n            [schism.node :as node]\n            [schism.impl.protocols :as proto])\n  #?(:clj (:import schism.impl.types.nested_map.NestedMap)))\n\n(deftest basic-IPC-ops\n  (testing \"Equiv for maps\"\n    (is (= (nmap/new-map) {}))\n    (is (= (nmap/new-map :a true) {:a true})))\n  (testing \"Empty for maps\"\n    (is (= (empty (nmap/new-map :a true)) (empty {:a true}))))\n  (testing \"Count for maps\"\n    (is (= (count (nmap/new-map :a true)) (count {:a true}))))\n  (testing \"Conj for maps\"\n    (is (= (conj (nmap/new-map) [:a true]) (conj {} [:a true])))\n    (is (= (conj (nmap/new-map :a true) [:a false]) (conj {:a true} [:a false])))))\n\n(deftest basic-IPS-ops\n  (testing \"dissoc\"\n    (is (= (dissoc (nmap/new-map :a true) :a) (dissoc {:a true} :a))))\n  (testing \"contains\"\n    (is (= (contains? (nmap/new-map :a true) :a) (contains? {:a true} :a))))\n  (testing \"get\"\n    (is (= (get (nmap/new-map :a true) :a) (get {:a true} :a)))))\n\n(deftest converge-test\n  (testing \"Converge after concurrent additions on another node.\"\n    (node/initialize-node! :converge-test-origin)\n    ;; Can only rely on millisecond time scales, so sleep 1 second\n    ;; between ops so that there's some non-zero passage of time\n    (let [transfer (-> (nmap/new-map :a true)\n                       (conj [:b 3]))\n          other (node/with-node :converge-test-other-node\n                  (conj transfer [:c :d]))\n          result (proto/synchronize transfer other)]\n      (is (= result {:a true :b 3 :c :d}))\n      (is (= {:a true :b 3 :c :d} (.-data result)))\n      (doseq [[k v] (.-vclock result)]\n        (is (#{:converge-test-origin :converge-test-other-node} k))\n        (is (instance? #?(:clj java.util.Date\n                          :cljs js/Date) v)))\n      (is (= #{:converge-test-origin :converge-test-other-node} (set (keys (.-vclock result)))))\n      (is (= (set (keys (.-data result))) (set (keys (.-birth-dots result)))))\n      (doseq [[key {node :a time :t} :as entry] (.-birth-dots result)]\n        (is (#{:a :b :c} key))\n        (is (#{:converge-test-origin :converge-test-other-node} node))\n        (is (instance? #?(:clj java.util.Date\n                          :cljs js/Date) time)))))\n  (testing \"Dissoc on another node mirrored locally after converge.\"\n    (node/initialize-node! :converge-test-origin)\n    (let [transfer (nmap/new-map :a true :b 3 :c :d)\n          other (node/with-node :converge-test-other-node\n                  (dissoc transfer :c))\n          result (proto/synchronize transfer other)]\n      (is (= other {:a true :b 3}))\n      (is (= result {:a true :b 3}))\n      (is (= (contains? (.-birth-dots other) :c) false))\n      (is (= (contains? (.-birth-dots result) :c) false)))))\n\n(deftest seqable-test\n  (testing \"Can turn an ORMWOT into a seq\"\n    (is (= (seq (nmap/new-map :a true :b 3 :c :d))\n           (seq (hash-map :a true :b 3 :c :d))))))\n\n(deftest ifn-test\n  (testing \"Can invoke an ORMWOT\"\n    (is (= ((nmap/new-map :a true :b 3 :c :d) :c)\n           ({:a true :b 3 :c :d} :c)))))\n\n(deftest string-test\n  (testing \"Prints to console readably, even though edn is verbose\"\n    (is (= (str (nmap/new-map :a true :b 3 :c :d))\n           (str (hash-map :a true :b 3 :c :d))))))\n\n(deftest serialization-test\n  (testing \"Round trip serialization generates the same structure.\"\n    (let [^NestedMap origin (-> (nmap/new-map :a true :b 3 :c :d)\n                          (conj [:d :quux])\n                          (dissoc :c))\n          ^NestedMap round-tripped (-> origin\n                                 pr-str\n                                 #?(:clj read-string\n                                    :cljs reader/read-string))]\n      (is (= (.-data origin) (.-data round-tripped)))\n      (is (= (.-vclock origin) (.-vclock round-tripped)))\n      (is (= (.-birth-dots origin) (.-birth-dots round-tripped))))))\n\n(deftest hashing-test\n  (testing \"Hashes to the same value as an equivalent hash-map\"\n    (is (= (hash (into (nmap/new-map) [[:a true] [:b 3] [:c :d]]))\n           (hash (into {} [[:a true] [:b 3] [:c :d]]))))))\n\n(deftest meta-test\n  (testing \"Metadata on ORMWOTs\"\n    (is (= (meta (with-meta (nmap/new-map) {:test :data}))\n           {:test :data}))))\n\n(deftest path-atomicity-test\n  (testing \"Concurrent modification of a subtree by two nodes converges the atomic values\"\n    (node/initialize-node! :path-atomicity-origin)\n    (let [original (-> (nmap/new-map :a true)\n                       (assoc-in [:b :c] true)\n                       (#(node/with-node :derivation-node-a\n                           (assoc-in % [:b :d] {:e 3 :f \"frog\"}))))\n          derivation-a (node/with-node :derivation-node-a\n                         (assoc-in original [:b :d :f] \"hog\"))\n          derivation-b (update-in original [:b :d :e] inc)\n          result (proto/synchronize derivation-b derivation-a)]\n      (is (= original {:a true\n                       :b {:c true\n                           :d {:e 3\n                               :f \"frog\"}}}))\n      (is (= result {:a true\n                     :b {:c true\n                         :d {:e 4\n                             :f \"hog\"}}})))))\n\n(deftest vector-conjs-compose\n  (testing \"Two conjs on different nodes yield both their conjs on convergence and do not overwrite\"\n    (node/initialize-node! :vector-conjs-origins)\n    (let [original (-> (nmap/new-map :a [1])\n                       (#(node/with-node :derivation-node-a\n                           (assoc-in % [:b] [2]))))\n          derivation-a (node/with-node :derivation-node-a\n                         (update-in original [:a] conj 2))\n          derivation-b (node/with-node :derivation-node-b\n                         (update-in original [:a] conj 3))\n          result (proto/synchronize derivation-b derivation-a)]\n      (is (= original {:a [1]\n                       :b [2]}))\n      (is (= derivation-a {:a [1 2]\n                           :b [2]}))\n      (is (= derivation-b {:a [1 3]\n                           :b [2]}))\n      (is (= result {:a [1 2 3]\n                     :b [2]})))))\n\n(deftest interesting-map-inits\n  (testing \"{-1 [0]}\"\n    (node/initialize-node! :interesting-map-inits)\n    (let [v (nmap/new-map -1 [0])]\n      (is (= {-1 [0]} (.-data v)))\n      (is (= :interesting-map-inits (get-in (.-birth-dots v) [-1 0 :a])))\n      (is (= -1 (get-in (.-birth-dots v) [-1 0 :i])))\n      (is (instance? #?(:clj java.util.Date\n                        :cljs js/Date) (get-in (.-birth-dots v) [-1 0 :t])))))\n  (testing \"{0 [[0] 0]}\"\n    (node/initialize-node! :interesting-map-inits)\n    (let [v (nmap/new-map 0 [[0] 0])]\n      (is (= {0 [[0] 0]} (.-data v)))\n      (is (= :interesting-map-inits (get-in (.-birth-dots v) [0 0 0 :a])))\n      (is (= -1 (get-in (.-birth-dots v) [0 0 0 :i])))\n      (is (instance? #?(:clj java.util.Date\n                        :cljs js/Date) (get-in (.-birth-dots v) [0 0 0 :t])))))\n  (testing \"{0 [0 0] -1 [0 0 0 0 0 0] -2 0}\"\n    (node/initialize-node! :interesting-map-inits)\n    (let [v (nmap/new-map 0 [0 0] -1 [0 0 0 0 0 0] -2 0)]\n      (is (= {0 [0 0] -1 [0 0 0 0 0 0] -2 0} (.-data v)))\n      (is (= :interesting-map-inits (get-in (.-birth-dots v) [0 0 :a])))\n      (is (= -1 (get-in (.-birth-dots v) [0 0 :i])))\n      (is (instance? #?(:clj java.util.Date\n                        :cljs js/Date) (get-in (.-birth-dots v) [0 0 :t])))))\n  (testing \"{0 [0 0] -1 [0 0 0 0 0 0] -2 0}\"\n    (node/initialize-node! :interesting-map-inits)\n    (let [v (nmap/new-map 0 [0 0] -1 [0 0 0 0 0 0] -2 0)]\n      (is (= {0 [0 0] -1 [0 0 0 0 0 0] -2 0} (.-data v)))\n      (is (= :interesting-map-inits (get-in (.-birth-dots v) [0 0 :a])))\n      (is (= -1 (get-in (.-birth-dots v) [0 0 :i])))\n      (is (instance? #?(:clj java.util.Date\n                        :cljs js/Date) (get-in (.-birth-dots v) [0 0 :t])))))\n  (testing \"{{} [0 0 0 0 0 0 0 0] #{} 0}\"\n    (node/initialize-node! :interesting-map-inits)\n    (let [v (nmap/new-map {} [0 0 0 0 0 0 0 0] #{} 0)]\n      (is (= {{} [0 0 0 0 0 0 0 0] #{} 0} (.-data v)))\n      (is (= :interesting-map-inits (get-in (.-birth-dots v) [{} 0 :a])))\n      (is (= -1 (get-in (.-birth-dots v) [{} 0 :i])))\n      (is (instance? #?(:clj java.util.Date\n                        :cljs js/Date) (get-in (.-birth-dots v) [{} 0 :t]))))))\n"
  },
  {
    "path": "test/schism/impl/types/nested_vector_test.cljc",
    "content": "(ns schism.impl.types.nested-vector-test\n  (:require #?(:clj [clojure.test :refer [deftest testing is]]\n               :cljs [cljs.test :refer [deftest testing is]])\n            #?(:cljs [cljs.reader :as reader])\n            [schism.impl.types.nested-vector :as nvector]\n            [schism.node :as node]\n            [schism.impl.protocols :as proto])\n  #?(:clj (:import schism.impl.types.nested_vector.NestedVector)))\n\n(deftest basic-IPC-ops\n  (testing \"Equiv for maps\"\n    (is (= (nvector/new-vector) []))\n    (is (= (nvector/new-vector :a true) [:a true])))\n  (testing \"Empty for maps\"\n    (is (= (empty (nvector/new-vector :a true)) (empty [:a true]))))\n  (testing \"Count for maps\"\n    (is (= (count (nvector/new-vector :a true)) (count [:a true]))))\n  (testing \"Conj for maps\"\n    (is (= (conj (nvector/new-vector) [:a true]) (conj [] [:a true])))\n    (is (= (conj (nvector/new-vector :a true) [:a false]) (conj [:a true] [:a false])))))\n\n(deftest basic-vector-ops\n  (testing \"peek\"\n    (is (= (peek (nvector/new-vector :a true)) (peek [:a true]))))\n  (testing \"pop\"\n    (is (= (pop (nvector/new-vector :a true)) (pop [:a true])))))\n\n(deftest converge-test\n  (testing \"Converge after concurrent additions on another node.\"\n    (node/initialize-node! :converge-test-origin)\n    ;; Can only rely on millisecond time scales, so sleep 1 second\n    ;; between ops so that there's some non-zero passage of time\n    (let [transfer (-> (nvector/new-vector :a true)\n                       (conj 3))\n          other (node/with-node :converge-test-other-node\n                  (conj transfer :d))\n          result (proto/synchronize transfer other)]\n      (is (= result [:a true 3 :d]))\n      (is (= [:a true 3 :d] (.-data result)))\n      (doseq [[k v] (.-vclock result)]\n        (is (#{:converge-test-origin :converge-test-other-node} k))\n        (is (instance? #?(:clj java.util.Date\n                          :cljs js/Date) v)))\n      (is (= #{:converge-test-origin :converge-test-other-node} (set (keys (.-vclock result)))))\n      (is (= (count (.-data result)) (count (.-insertions result))))\n      (doseq [{node :a time :t} (.-insertions result)]\n        (is (#{:converge-test-origin :converge-test-other-node} node))\n        (is (instance? #?(:clj java.util.Date\n                          :cljs js/Date) time)))))\n  (testing \"Pop on another node mirrored locally after converge.\"\n    (node/initialize-node! :converge-test-origin)\n    (let [transfer (nvector/new-vector :a true :b 3 :c :d)\n          other (node/with-node :converge-test-other-node\n                  (pop transfer))\n          result (proto/synchronize transfer other)]\n      (is (= other [:a true :b 3 :c]))\n      (is (= result [:a true :b 3 :c]))\n      (is (= (count (.-insertions other)) 5))\n      (is (= (count (.-insertions result)) 5)))))\n\n(deftest seqable-test\n  (testing \"Can turn an nested vector into a seq\"\n    (is (= (seq (nvector/new-vector :a true :b 3 :c :d))\n           (seq (vector :a true :b 3 :c :d))))))\n\n(deftest ifn-test\n  (testing \"Can invoke a vector\"\n    (is (= ((nvector/new-vector :a true :b 3 :c :d) 0)\n           ([:a true :b 3 :c :d] 0)\n           :a))))\n\n(deftest string-test\n  (testing \"Prints to console readably, even though edn is verbose\"\n    (is (= (str (nvector/new-vector :a true :b 3 :c :d))\n           (str (vector :a true :b 3 :c :d))))))\n\n(deftest serialization-test\n  (testing \"Round trip serialization generates the same structure.\"\n    (let [^NestedVector origin (-> (nvector/new-vector :a true :b 3 :c :d)\n                                   (conj [:d :quux])\n                                   pop)\n          ^NestedVector round-tripped (-> origin\n                                          pr-str\n                                          #?(:clj read-string\n                                             :cljs reader/read-string))]\n      (is (= (.-data origin) (.-data round-tripped)))\n      (is (= (.-vclock origin) (.-vclock round-tripped)))\n      (is (= (.-insertions origin) (.-insertions round-tripped))))))\n\n(deftest hashing-test\n  (testing \"Hashes to the same value as an equivalent vector\"\n    (is (= (hash (into (nvector/new-vector) [[:a true] [:b 3] [:c :d]]))\n           (hash (into [] [[:a true] [:b 3] [:c :d]]))))))\n\n(deftest meta-test\n  (testing \"Metadata on nested vectors\"\n    (is (= (meta (with-meta (nvector/new-vector) {:test :data}))\n           {:test :data}))))\n\n(deftest path-atomicity-test\n  (testing \"Concurrent modification of a subtree by two nodes converges the atomic values\"\n    (node/initialize-node! :path-atomicity-origin)\n    (let [original (-> (nvector/new-vector :a true)\n                       (assoc-in [2 :b] true)\n                       (#(node/with-node :derivation-node-a\n                           (assoc-in % [3 :c] {:e 3 :f \"frog\"}))))\n          derivation-a (node/with-node :derivation-node-a\n                         (assoc-in original [3 :c :f] \"hog\"))\n          derivation-b (update-in original [3 :c :e] inc)\n          result (proto/synchronize derivation-b derivation-a)]\n      (is (= original [:a true {:b true} {:c {:e 3 :f \"frog\"}}]))\n      (is (= result [:a true {:b true} {:c {:e 4 :f \"hog\"}}])))))\n\n(deftest vector-conjs-compose\n  (testing \"Two conjs on different nodes yield both their conjs on convergence and do not overwrite\"\n    (node/initialize-node! :vector-conjs-origins)\n    (let [original (-> (nvector/new-vector :a [1])\n                       (#(node/with-node :derivation-node-a\n                           (assoc-in % [2] [2]))))\n          derivation-a (node/with-node :derivation-node-a\n                         (update-in original [1] conj 2))\n          derivation-b (node/with-node :derivation-node-b\n                         (update-in original [1] conj 3))\n          result (proto/synchronize derivation-b derivation-a)]\n      (is (= original [:a [1] [2]]))\n      (is (= derivation-a [:a [1 2] [2]]))\n      (is (= derivation-b [:a [1 3] [2]]))\n      (is (= result [:a [1 2 3] [2]])))))\n\n(deftest interesting-vector-inits\n  (testing \"Empty vector stacked 2 deep\"\n    (node/initialize-node! :interesting-vector-inits)\n    (let [v (nvector/new-vector [[]])]\n      (is (= [[[]]] (.-data v)))\n      (is (= :interesting-vector-inits (get-in (.-insertions v) [0 0 :a])))\n      (is (= -1 (get-in (.-insertions v) [0 0 :i])))\n      (is (instance? #?(:clj java.util.Date\n                        :cljs js/Date) (get-in (.-insertions v) [0 0 :t]))))))\n"
  },
  {
    "path": "test/schism/impl/types/set_test.cljc",
    "content": "(ns schism.impl.types.set-test\n  (:require #?(:clj [clojure.test :refer [deftest testing is are]]\n               :cljs [cljs.test :refer [deftest testing is are]])\n            #?(:cljs [cljs.reader :as reader])\n            [schism.impl.types.set :as sset]\n            [schism.node :as node]\n            [schism.impl.protocols :as proto])\n  #?(:clj (:import schism.impl.types.set.Set)))\n\n(deftest basic-IPC-ops\n  (testing \"Equiv for sets\"\n    (is (= (sset/new-set) #{}))\n    (is (= (sset/new-set :a) #{:a})))\n  (testing \"Empty for sets\"\n    (is (= (empty (sset/new-set :a)) (empty #{:a}))))\n  (testing \"Count for sets\"\n    (is (= (count (sset/new-set :a)) (count #{:a}))))\n  (testing \"Conj for sets\"\n    (is (= (conj (sset/new-set) :a) (conj #{} :a)))\n    (is (= (conj (sset/new-set :a) :a) (conj #{:a} :a)))))\n\n(deftest basic-IPS-ops\n  (testing \"disjoin\"\n    (is (= (disj (sset/new-set :a) :a) (disj #{:a} :a))))\n  (testing \"contains\"\n    (is (= (contains? (sset/new-set :a) :a) (contains? #{:a} :a))))\n  (testing \"get\"\n    (is (= (get (sset/new-set :a) :a) (get #{:a} :a)))))\n\n(deftest converge-test\n  (testing \"Converge after concurrent additions on another node.\"\n    (node/initialize-node! :converge-test-origin)\n    ;; Can only rely on millisecond time scales, so sleep 1 second\n    ;; between ops so that there's some non-zero passage of time\n    (let [transfer (-> (sset/new-set :a)\n                       (conj :b))\n          other (node/with-node :converge-test-other-node\n                  (conj transfer :c))\n          result (proto/synchronize transfer other)]\n      (is (= result #{:a :b :c}))\n      (is (= #{:a :b :c} (.-data result)))\n      (doseq [[k v] (.-vclock result)]\n        (is (#{:converge-test-origin :converge-test-other-node} k))\n        (is (instance? #?(:clj java.util.Date\n                          :cljs js/Date) v)))\n      (is (= #{:converge-test-origin :converge-test-other-node} (set (keys (.-vclock result)))))\n      (is (= (.-data result) (set (keys (.-birth-dots result)))))\n      (doseq [[element [node time]] (.-birth-dots result)]\n        (is (#{:a :b :c} element))\n        (is (#{:converge-test-origin :converge-test-other-node} node))\n        (is (instance? #?(:clj java.util.Date\n                          :cljs js/Date) time)))))\n  (testing \"Disj on another node mirrored locally after converge.\"\n    (node/initialize-node! :converge-test-origin)\n    (let [transfer (sset/new-set :a :b :c)\n          other (node/with-node :converge-test-other-node\n                  (disj transfer :c))\n          result (proto/synchronize transfer other)]\n      (is (= other #{:a :b}))\n      (is (= result #{:a :b}))))\n  (testing \"Concurrent converges will not resolve to a\n  mutually-exclusive addition when vector clocks will support it.\"\n    (node/initialize-node! :converge-test-origin)\n    (let [transfer (-> (sset/new-set :a)\n                       (conj :b))\n          iterate (conj transfer :d)\n          other (node/with-node :converge-test-other-node\n                  (conj transfer :c))\n          result (proto/synchronize iterate other)]\n      (is (= result #{:a :b :c :d})))))\n\n(deftest seqable-test\n  (testing \"Can turn an ORSWOT into a seq\"\n    (is (= (seq (sset/new-set :a :b :c))\n           (seq (hash-set :a :b :c))))))\n\n(deftest ifn-test\n  (testing \"Can invoke an ORSWOT\"\n    (is (= ((sset/new-set :a :b :c) :c)\n           (#{:a :b :c} :c)))))\n\n(deftest string-test\n  (testing \"Prints to console readably, even though edn is verbose\"\n    (is (= (str (sset/new-set :a :b :c))\n           (str (hash-set :a :b :c))))))\n\n(deftest serialization-test\n  (testing \"Round trip serialization generates the same structure.\"\n    (let [^Set origin (-> (sset/new-set :a :b :c)\n                          (conj :d)\n                          (disj :c))\n          ^Set round-tripped (-> origin\n                                 pr-str\n                                 #?(:clj read-string\n                                    :cljs reader/read-string))]\n      (is (= (.-data origin) (.-data round-tripped)))\n      (is (= (.-vclock origin) (.-vclock round-tripped)))\n      (is (= (.-birth-dots origin) (.-birth-dots round-tripped))))))\n\n(deftest hashing-test\n  (testing \"Hashes to the same value as an equivalent hash-set\"\n    (is (= (hash (into (sset/new-set) [:a :b :c]))\n           (hash (into #{} [:a :b :c]))))))\n\n(deftest meta-test\n  (testing \"Metadata on ORSWOTs\"\n    (is (= (meta (with-meta (sset/new-set) {:test :data}))\n           {:test :data}))))\n"
  },
  {
    "path": "test/schism/impl/types/vector_test.cljc",
    "content": "(ns schism.impl.types.vector-test\n  (:require #?(:clj [clojure.test :refer [deftest testing is]]\n               :cljs [cljs.test :refer [deftest testing is]])\n            #?(:cljs [cljs.reader :as reader])\n            [schism.impl.types.vector :as svector]\n            [schism.node :as node]\n            [schism.impl.protocols :as proto])\n  #?(:clj (:import schism.impl.types.vector.ConvergentVector)))\n\n(deftest basic-IPC-ops\n  (testing \"Equiv for vectors\"\n    (is (= (svector/new-vector) []))\n    (is (= (svector/new-vector :a true) [:a true])))\n  (testing \"Empty for vectors\"\n    (is (= (empty (svector/new-vector :a true)) (empty [:a true]))))\n  (testing \"Count for vectors\"\n    (is (= (count (svector/new-vector :a true)) (count [:a true]))))\n  (testing \"Conj for vectors\"\n    (is (= (conj (svector/new-vector) [:a true]) (conj [] [:a true])))\n    (is (= (conj (svector/new-vector :a true) [:a false]) (conj [:a true] [:a false])))))\n\n(deftest basic-vector-ops\n  (testing \"conj\"\n    (is (= (conj (svector/new-vector :a true) :a) (conj [:a true] :a))))\n  (testing \"peek\"\n    (is (= (peek (svector/new-vector :a true)) (peek [:a true]))))\n  (testing \"pop\"\n    (is (= (pop (svector/new-vector :a true)) (pop [:a true])))))\n\n(deftest converge-test\n  (testing \"Converge after concurrent additions on another node.\"\n    (node/initialize-node! :converge-test-origin)\n    ;; Can only rely on millisecond time scales, so sleep 1 second\n    ;; between ops so that there's some non-zero passage of time\n    (let [transfer (-> (svector/new-vector true :a)\n                       (conj [:b 3]))\n          other (node/with-node :converge-test-other-node\n                  (conj transfer [:c :d]))\n          result (proto/synchronize transfer other)]\n      (is (= other [true :a [:b 3] [:c :d]]))\n      (is (= result [true :a [:b 3] [:c :d]]))))\n  (testing \"Pop on another node mirrored locally after converge.\"\n    (node/initialize-node! :converge-test-origin)\n    (let [transfer (svector/new-vector :a true :b 3 :c :d)\n          other (node/with-node :converge-test-other-node\n                  (pop transfer))\n          result (proto/synchronize transfer other)]\n      (is (= other [:a true :b 3 :c]))\n      (is (= result [:a true :b 3 :c])))))\n\n(deftest seqable-test\n  (testing \"Can turn a CVECTOR into a seq\"\n    (is (= (seq (svector/new-vector :a true :b 3 :c :d))\n           (seq (vector :a true :b 3 :c :d))))))\n\n(deftest string-test\n  (testing \"Prints to console readably, even though edn is verbose\"\n    (is (= (str (svector/new-vector :a true :b 3 :c :d))\n           (str (vector :a true :b 3 :c :d))))))\n\n(deftest serialization-test\n  (testing \"Round trip serialization generates the same structure.\"\n    (let [^ConvergentVector origin (-> (svector/new-vector :a true :b 3 :c :d)\n                                       (conj [:d :quux])\n                                       pop)\n          ^ConvergentVector round-tripped (-> origin\n                                              pr-str\n                                              #?(:clj read-string\n                                                 :cljs reader/read-string))]\n      (is (= (.-data origin) (.-data round-tripped)))\n      (is (= (.-vclock origin) (.-vclock round-tripped)))\n      (is (= (.-insertions origin) (.-insertions round-tripped))))))\n\n(deftest hashing-test\n  (testing \"Hashes to the same value as an equivalent vector\"\n    (is (= (hash (into (svector/new-vector) [[:a true] [:b 3] [:c :d]]))\n           (hash (into [] [[:a true] [:b 3] [:c :d]]))))))\n\n(deftest meta-test\n  (testing \"Metadata on CVECTORs\"\n    (is (= (meta (with-meta (svector/new-vector) {:test :data}))\n           {:test :data}))))\n"
  },
  {
    "path": "test/schism/impl/vector_clock_test.cljc",
    "content": "(ns schism.impl.vector-clock-test\n  (:require #?(:clj [clojure.test :refer [deftest testing is]]\n               :cljs [cljs.test :refer [deftest testing is]])\n            [schism.impl.vector-clock :as vc]\n            [schism.impl.protocols :as proto]\n            [schism.node :as node]))\n\n\n(defrecord SimpleClocked [last-time vclock]\n  proto/Vclocked\n  (get-clock [_] vclock)\n  (with-clock [this new-clock]\n    (assoc this :vclock new-clock)))\n\n(deftest update-clock-test\n  (node/initialize-node! :clock-test-node)\n  (let [test (->SimpleClocked nil {})\n        updated (vc/update-clock time test\n                                 (assoc test :last-time time))\n        time (:last-time updated)]\n    (is (= {:clock-test-node time} (proto/get-clock updated)))\n    (is #?(:clj (instance? java.util.Date time)\n           :cljs true))))\n"
  },
  {
    "path": "test/schism/node_test.cljc",
    "content": "(ns schism.node-test\n  (:require #?(:clj [clojure.test :refer [deftest testing is]]\n               :cljs [cljs.test :refer [deftest testing is]])\n            [schism.node :as node])\n  #?(:cljs (:require-macros [schism.node :as node])))\n\n(deftest initialize-node!-test\n  (testing \"With no arg\"\n    (node/initialize-node!)\n    (is (some? node/*current-node*)))\n  (testing \"with an arg\"\n    (node/initialize-node! \"a string node id\")\n    (is (= \"a string node id\" node/*current-node*))))\n\n(deftest with-node-test\n  (testing \"with-node clobbers other set values for current-node\"\n    (node/initialize-node! :with-node-id)\n    (is (= :with-node-id node/*current-node*))\n    (node/with-node :another-id\n      (is (not= :with-node-id node/*current-node*))\n      (is (= :another-id node/*current-node*)))))\n"
  },
  {
    "path": "test/schism/test.cljc",
    "content": "(ns schism.test\n  (:require #?(:cljs [doo.runner :refer-macros [doo-tests]])\n            schism.node-test\n            schism.core-test\n            schism.impl.vector-clock-test\n            schism.impl.types.set-test\n            schism.impl.types.map-test\n            schism.impl.types.list-test\n            schism.impl.types.vector-test\n            schism.impl.types.nested-map-test\n            schism.impl.types.nested-vector-test))\n\n#?(:cljs (doo-tests 'schism.node-test\n                    'schism.core-test\n                    'schism.impl.vector-clock-test\n                    'schism.impl.types.set-test\n                    'schism.impl.types.map-test\n                    'schism.impl.types.list-test\n                    'schism.impl.types.vector-test\n                    'schism.impl.types.nested-map-test\n                    'schism.impl.types.nested-vector-test))\n"
  }
]