BUG-7464: Initial import of java-concurrent-hash-trie-map 59/49859/3
authorRobert Varga <rovarga@cisco.com>
Thu, 29 Dec 2016 12:11:38 +0000 (13:11 +0100)
committerRobert Varga <nite@hq.sk>
Tue, 3 Jan 2017 12:36:34 +0000 (12:36 +0000)
This is the initial import of java-concurrent-hash-trie-map, as available
at https://github.com/rovarga/java-concurrent-hash-trie-map/tree/odl-0.2.23.

The upstream from which this code was forked is dead for all intents
and purposes, as demonstrated by lack of reaction to multiple pull requests
over the past year, including the critical correctness fix needed
in OpenDaylight.

This fork will be evolved to clean up the code base according to OpenDaylight
coding practices and to take advantage of both Java 8 and Guava.

Change-Id: I16e6e8c724f391e262e7b192c2e1f875364785db
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
32 files changed:
third-party/triemap/LICENSE-2.0.html [new file with mode: 0644]
third-party/triemap/README.md [new file with mode: 0644]
third-party/triemap/pom.xml [new file with mode: 0644]
third-party/triemap/src/main/java/com/romix/scala/None.java [new file with mode: 0644]
third-party/triemap/src/main/java/com/romix/scala/Option.java [new file with mode: 0644]
third-party/triemap/src/main/java/com/romix/scala/Some.java [new file with mode: 0644]
third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/BasicNode.java [new file with mode: 0644]
third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/CNodeBase.java [new file with mode: 0644]
third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/Gen.java [new file with mode: 0644]
third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/INodeBase.java [new file with mode: 0644]
third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/ListMap.java [new file with mode: 0644]
third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/MainNode.java [new file with mode: 0644]
third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/Pair.java [new file with mode: 0644]
third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/TrieMap.java [new file with mode: 0644]
third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestCNodeFlagCollision.java [new file with mode: 0644]
third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestCNodeInsertionIncorrectOrder.java [new file with mode: 0644]
third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestConcurrentMapPutIfAbsent.java [new file with mode: 0644]
third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestConcurrentMapRemove.java [new file with mode: 0644]
third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestConcurrentMapReplace.java [new file with mode: 0644]
third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestDelete.java [new file with mode: 0644]
third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestHashCollisions.java [new file with mode: 0644]
third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestHashCollisionsRemove.java [new file with mode: 0644]
third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestHashCollisionsRemoveIterator.java [new file with mode: 0644]
third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestHelper.java [new file with mode: 0644]
third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestInsert.java [new file with mode: 0644]
third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestInstantiationSpeed.java [new file with mode: 0644]
third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestMapIterator.java [new file with mode: 0644]
third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestMultiThreadAddDelete.java [new file with mode: 0644]
third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestMultiThreadInserts.java [new file with mode: 0644]
third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestMultiThreadMapIterator.java [new file with mode: 0644]
third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestReadOnlyAndUpdatableIterators.java [new file with mode: 0644]
third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestSerialization.java [new file with mode: 0644]

diff --git a/third-party/triemap/LICENSE-2.0.html b/third-party/triemap/LICENSE-2.0.html
new file mode 100644 (file)
index 0000000..2320883
--- /dev/null
@@ -0,0 +1,235 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+    <link rel="stylesheet" href="LICENSE-2.0_fichiers/style.css" type="text/css">
+    <meta name="author" content="The Apache Software Foundation">
+    <meta name="email" content="apache.AT.apache.DOT.org">
+    <title>Apache License, Version 2.0 - The Apache Software Foundation</title>
+</head>
+<body>
+<p align="center">
+    Apache License<br>
+    Version 2.0, January 2004<br>
+    <a href="http://www.apache.org/licenses/">http://www.apache.org/licenses/</a>
+</p>
+
+<p>
+    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+</p>
+
+<p><b><a name="definitions">1. Definitions</a></b>.</p>
+
+<p>
+    "License" shall mean the terms and conditions for use, reproduction,
+    and distribution as defined by Sections 1 through 9 of this document.
+</p>
+
+<p>
+    "Licensor" shall mean the copyright owner or entity authorized by
+    the copyright owner that is granting the License.
+</p>
+
+<p>
+    "Legal Entity" shall mean the union of the acting entity and all
+    other entities that control, are controlled by, or are under common
+    control with that entity. For the purposes of this definition,
+    "control" means (i) the power, direct or indirect, to cause the
+    direction or management of such entity, whether by contract or
+    otherwise, or (ii) ownership of fifty percent (50%) or more of the
+    outstanding shares, or (iii) beneficial ownership of such entity.
+</p>
+
+<p>
+    "You" (or "Your") shall mean an individual or Legal Entity
+    exercising permissions granted by this License.
+</p>
+
+<p>
+    "Source" form shall mean the preferred form for making modifications,
+    including but not limited to software source code, documentation
+    source, and configuration files.
+</p>
+
+<p>
+    "Object" form shall mean any form resulting from mechanical
+    transformation or translation of a Source form, including but
+    not limited to compiled object code, generated documentation,
+    and conversions to other media types.
+</p>
+
+<p>
+    "Work" shall mean the work of authorship, whether in Source or
+    Object form, made available under the License, as indicated by a
+    copyright notice that is included in or attached to the work
+    (an example is provided in the Appendix below).
+</p>
+
+<p>
+    "Derivative Works" shall mean any work, whether in Source or Object
+    form, that is based on (or derived from) the Work and for which the
+    editorial revisions, annotations, elaborations, or other modifications
+    represent, as a whole, an original work of authorship. For the purposes
+    of this License, Derivative Works shall not include works that remain
+    separable from, or merely link (or bind by name) to the interfaces of,
+    the Work and Derivative Works thereof.
+</p>
+
+<p>
+    "Contribution" shall mean any work of authorship, including
+    the original version of the Work and any modifications or additions
+    to that Work or Derivative Works thereof, that is intentionally
+    submitted to Licensor for inclusion in the Work by the copyright owner
+    or by an individual or Legal Entity authorized to submit on behalf of
+    the copyright owner. For the purposes of this definition, "submitted"
+    means any form of electronic, verbal, or written communication sent
+    to the Licensor or its representatives, including but not limited to
+    communication on electronic mailing lists, source code control systems,
+    and issue tracking systems that are managed by, or on behalf of, the
+    Licensor for the purpose of discussing and improving the Work, but
+    excluding communication that is conspicuously marked or otherwise
+    designated in writing by the copyright owner as "Not a Contribution."
+</p>
+
+<p>
+    "Contributor" shall mean Licensor and any individual or Legal Entity
+    on behalf of whom a Contribution has been received by Licensor and
+    subsequently incorporated within the Work.
+</p>
+
+<p><b><a name="copyright">2. Grant of Copyright License</a></b>.
+    Subject to the terms and conditions of
+    this License, each Contributor hereby grants to You a perpetual,
+    worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+    copyright license to reproduce, prepare Derivative Works of,
+    publicly display, publicly perform, sublicense, and distribute the
+    Work and such Derivative Works in Source or Object form.
+</p>
+
+<p><b><a name="patent">3. Grant of Patent License</a></b>.
+    Subject to the terms and conditions of
+    this License, each Contributor hereby grants to You a perpetual,
+    worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+    (except as stated in this section) patent license to make, have made,
+    use, offer to sell, sell, import, and otherwise transfer the Work,
+    where such license applies only to those patent claims licensable
+    by such Contributor that are necessarily infringed by their
+    Contribution(s) alone or by combination of their Contribution(s)
+    with the Work to which such Contribution(s) was submitted. If You
+    institute patent litigation against any entity (including a
+    cross-claim or counterclaim in a lawsuit) alleging that the Work
+    or a Contribution incorporated within the Work constitutes direct
+    or contributory patent infringement, then any patent licenses
+    granted to You under this License for that Work shall terminate
+    as of the date such litigation is filed.
+</p>
+
+<p><b><a name="redistribution">4. Redistribution</a></b>.
+    You may reproduce and distribute copies of the
+    Work or Derivative Works thereof in any medium, with or without
+    modifications, and in Source or Object form, provided that You
+    meet the following conditions:
+</p>
+<ol type="a">
+    <li>You must give any other recipients of the Work or
+        Derivative Works a copy of this License; and
+        <br> <br></li>
+
+    <li>You must cause any modified files to carry prominent notices
+        stating that You changed the files; and
+        <br> <br></li>
+
+    <li>You must retain, in the Source form of any Derivative Works
+        that You distribute, all copyright, patent, trademark, and
+        attribution notices from the Source form of the Work,
+        excluding those notices that do not pertain to any part of
+        the Derivative Works; and
+        <br> <br></li>
+
+    <li>If the Work includes a "NOTICE" text file as part of its
+        distribution, then any Derivative Works that You distribute must
+        include a readable copy of the attribution notices contained
+        within such NOTICE file, excluding those notices that do not
+        pertain to any part of the Derivative Works, in at least one
+        of the following places: within a NOTICE text file distributed
+        as part of the Derivative Works; within the Source form or
+        documentation, if provided along with the Derivative Works; or,
+        within a display generated by the Derivative Works, if and
+        wherever such third-party notices normally appear. The contents
+        of the NOTICE file are for informational purposes only and
+        do not modify the License. You may add Your own attribution
+        notices within Derivative Works that You distribute, alongside
+        or as an addendum to the NOTICE text from the Work, provided
+        that such additional attribution notices cannot be construed
+        as modifying the License.
+    </li>
+</ol>
+You may add Your own copyright statement to Your modifications and
+may provide additional or different license terms and conditions
+for use, reproduction, or distribution of Your modifications, or
+for any such Derivative Works as a whole, provided Your use,
+reproduction, and distribution of the Work otherwise complies with
+the conditions stated in this License.
+
+<p><b><a name="contributions">5. Submission of Contributions</a></b>.
+    Unless You explicitly state otherwise,
+    any Contribution intentionally submitted for inclusion in the Work
+    by You to the Licensor shall be under the terms and conditions of
+    this License, without any additional terms or conditions.
+    Notwithstanding the above, nothing herein shall supersede or modify
+    the terms of any separate license agreement you may have executed
+    with Licensor regarding such Contributions.
+</p>
+
+<p><b><a name="trademarks">6. Trademarks</a></b>.
+    This License does not grant permission to use the trade
+    names, trademarks, service marks, or product names of the Licensor,
+    except as required for reasonable and customary use in describing the
+    origin of the Work and reproducing the content of the NOTICE file.
+</p>
+
+<p><b><a name="no-warranty">7. Disclaimer of Warranty</a></b>.
+    Unless required by applicable law or
+    agreed to in writing, Licensor provides the Work (and each
+    Contributor provides its Contributions) on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+    implied, including, without limitation, any warranties or conditions
+    of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+    PARTICULAR PURPOSE. You are solely responsible for determining the
+    appropriateness of using or redistributing the Work and assume any
+    risks associated with Your exercise of permissions under this License.
+</p>
+
+<p><b><a name="no-liability">8. Limitation of Liability</a></b>.
+    In no event and under no legal theory,
+    whether in tort (including negligence), contract, or otherwise,
+    unless required by applicable law (such as deliberate and grossly
+    negligent acts) or agreed to in writing, shall any Contributor be
+    liable to You for damages, including any direct, indirect, special,
+    incidental, or consequential damages of any character arising as a
+    result of this License or out of the use or inability to use the
+    Work (including but not limited to damages for loss of goodwill,
+    work stoppage, computer failure or malfunction, or any and all
+    other commercial damages or losses), even if such Contributor
+    has been advised of the possibility of such damages.
+</p>
+
+<p><b><a name="additional">9. Accepting Warranty or Additional Liability</a></b>.
+    While redistributing
+    the Work or Derivative Works thereof, You may choose to offer,
+    and charge a fee for, acceptance of support, warranty, indemnity,
+    or other liability obligations and/or rights consistent with this
+    License. However, in accepting such obligations, You may act only
+    on Your own behalf and on Your sole responsibility, not on behalf
+    of any other Contributor, and only if You agree to indemnify,
+    defend, and hold each Contributor harmless for any liability
+    incurred by, or claims asserted against, such Contributor by reason
+    of your accepting any such warranty or additional liability.
+</p>
+
+<p>
+    END OF TERMS AND CONDITIONS
+</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/third-party/triemap/README.md b/third-party/triemap/README.md
new file mode 100644 (file)
index 0000000..d1c3b56
--- /dev/null
@@ -0,0 +1,83 @@
+About
+=============================
+
+This is a Java port of a concurrent trie hash map implementation from the Scala collections library. It is almost a line-by-line 
+conversion from Scala to Java.
+
+Idea + implementation techniques can be found in these reports written by Aleksandar Prokopec:
+   * http://infoscience.epfl.ch/record/166908/files/ctries-techreport.pdf - this is a nice introduction to Ctries, along with a correctness proof
+   * http://lamp.epfl.ch/~prokopec/ctries-snapshot.pdf - a more up-to-date writeup which describes the snapshot operation
+
+The original Scala implementation can be found here and is a part of scala.collection.concurrent:
+   *   [Scala implementation](https://github.com/scala/scala/blob/930c85d6c96507d798d1847ea078eebf93dc0acb/src/library/scala/collection/concurrent/TrieMap.scala)
+
+Some of the tests and implementation details were borrowed from this project:
+   *  https://github.com/flegall/concurrent-hash-trie
+
+Implementation status : 
+   *   The given implementation is complete and implements all features of the original Scala implementation including support for 
+   snapshots.
+   *   Wherever necessary, code was adapted to be more easily usable in Java, e.g. it returns Objects instead of Option<V> as 
+   many methods of Scala's collections do.   
+   *   This class implements all the ConcurrentMap & Iterator methods and passes all the tests. Can be used as a drop-in replacement
+       for usual Java maps, including ConcurrentHashMap.
+
+
+What is a concurrent trie hash map also known as ctrie?
+========================================================
+ctrie is a lock-Free Concurrent Hash Array Mapped Trie.
+
+A concurrent hash-trie or Ctrie is a concurrent thread-safe lock-free implementation of a hash array mapped trie.
+It is used to implement the concurrent map abstraction. It has particularly scalable concurrent insert and remove operations 
+and is memory-efficient. 
+
+It supports O(1), atomic, lock-free snapshots which are used to implement linearizable lock-free size, iterator and clear operations. 
+The cost of evaluating the (lazy) snapshot is distributed across subsequent updates, thus making snapshot evaluation horizontally scalable.
+
+The original Scala-based implementation of the Ctrie is a part of the Scala standard library since the version 2.10.
+
+More info about Ctries:
+
+- http://infoscience.epfl.ch/record/166908/files/ctries-techreport.pdf - this is a nice introduction to Ctries, along with a correctness proof
+- http://lamp.epfl.ch/~prokopec/ctries-snapshot.pdf - a more up-to-date writeup (more coherent with the current version of the code) which describes the snapshot operation
+       
+
+License
+===============================
+
+This library is licensed under the Apache 2.0 license.
+
+
+Usage
+===============================
+
+Usage of this library is very simple. Simply import the class com.romix.scala.collection.concurrent.TrieMap and use it as a usual Map.
+    
+    import com.romix.scala.collection.concurrent.TrieMap;
+    
+    Map myMap = new TrieMap <Object, Object> ();
+    myMap.put("key", "value");
+    
+
+Building the library
+===============================
+
+Use a usual `mvn clean install`
+
+Using the library with Maven projects
+=====================================
+The prebuilt binaries of the library are available from Maven central. Please use the following dependency in your POM files:
+
+               <dependency>
+                       <groupId>com.github.romix</groupId>
+                       <artifactId>java-concurrent-hash-trie-map</artifactId>
+                       <version>0.2.1</version>
+               </dependency>
+
+
+External dependencies
+=====================================
+This library is self-contained. It does not depend on any additional libraries. In particular, it does not require the rather big Scala's 
+standard library to be used.
+
diff --git a/third-party/triemap/pom.xml b/third-party/triemap/pom.xml
new file mode 100644 (file)
index 0000000..10d77a9
--- /dev/null
@@ -0,0 +1,90 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.sonatype.oss</groupId>
+               <artifactId>oss-parent</artifactId>
+               <version>7</version>
+       </parent>
+       <groupId>com.github.romix</groupId>
+       <artifactId>java-concurrent-hash-trie-map</artifactId>
+       <version>0.2.23-ODL</version>
+       <name>TrieMap</name>
+       <description>Java implementation of a concurrent trie hash map from Scala collections library</description>
+       <packaging>bundle</packaging>
+       <url>https://github.com/romix/java-concurrent-hash-trie-map</url>
+       <licenses>
+               <license>
+                       <name>The Apache Software License, Version 2.0</name>
+                       <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+                       <distribution>repo</distribution>
+               </license>
+       </licenses>
+       <scm>
+               <url>https://github.com/romix/java-concurrent-hash-trie-map</url>
+               <connection>scm:git:https://github.com/romix/java-concurrent-hash-trie-map.git</connection>
+               <developerConnection>scm:git:https://github.com/romix/java-concurrent-hash-trie-map.git</developerConnection>
+               <tag>java-concurrent-hash-trie-map-0.2.23</tag>
+       </scm>
+       <developers>
+               <developer>
+                       <id>romix</id>
+                       <name>Roman Levenstein</name>
+                       <email>romixlev@gmail.com</email>
+               </developer>
+       </developers>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                               <version>2.3.2</version>
+                               <configuration>
+                                       <source>1.6</source>
+                                       <target>1.6</target>
+                                       <encoding>iso8859-1</encoding>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-release-plugin</artifactId>
+                               <version>2.5</version>
+                               <configuration>
+                                       <checkModificationExcludes>
+                                               <checkModificationExclude>pom.xml</checkModificationExclude>
+                                       </checkModificationExcludes>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.felix</groupId>
+                               <artifactId>maven-bundle-plugin</artifactId>
+                               <version>2.4.0</version>
+                               <extensions>true</extensions>
+                               <configuration>
+                                       <instructions>
+                                               <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+                                       </instructions>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+       <dependencies>
+               <dependency>
+                       <groupId>junit</groupId>
+                       <artifactId>junit</artifactId>
+                       <version>4.9</version>
+                       <scope>test</scope>
+               </dependency>
+       </dependencies>
+       <distributionManagement>
+               <repository>
+                       <id>sonatype-nexus-staging</id>
+                       <name>Nexus Staging Repository</name>
+                       <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
+               </repository>
+               <snapshotRepository>
+                       <id>sonatype-nexus-snapshots</id>
+                       <name>Nexus Snapshots Repository</name>
+                       <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
+               </snapshotRepository>
+       </distributionManagement>
+</project>
diff --git a/third-party/triemap/src/main/java/com/romix/scala/None.java b/third-party/triemap/src/main/java/com/romix/scala/None.java
new file mode 100644 (file)
index 0000000..ba7d648
--- /dev/null
@@ -0,0 +1,12 @@
+package com.romix.scala;
+
+/**
+ * Mimic None in Scala
+ *  
+ * @author Roman Levenstein <romixlev@gmail.com>
+ *
+ * @param <V>
+ */
+public class None<V> extends Option<V>{
+
+}
diff --git a/third-party/triemap/src/main/java/com/romix/scala/Option.java b/third-party/triemap/src/main/java/com/romix/scala/Option.java
new file mode 100644 (file)
index 0000000..833ef5f
--- /dev/null
@@ -0,0 +1,26 @@
+package com.romix.scala;
+
+/**
+ * Mimic Option in Scala
+ *  
+ * @author Roman Levenstein <romixlev@gmail.com>
+ *
+ * @param <V>
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+public class Option<V> {
+    static None none = new None();
+    public static <V> Option<V> makeOption(V o){
+        if(o!=null)
+            return new Some<V>(o);
+        else
+            return (Option<V>)none;
+    }
+
+    public static <V> Option<V> makeOption(){
+        return (Option<V>)none;
+    }
+    public boolean nonEmpty () {
+        return false;
+    }
+}
diff --git a/third-party/triemap/src/main/java/com/romix/scala/Some.java b/third-party/triemap/src/main/java/com/romix/scala/Some.java
new file mode 100644 (file)
index 0000000..6268600
--- /dev/null
@@ -0,0 +1,23 @@
+package com.romix.scala;
+
+/**
+ * Mimic Some in Scala
+ *  
+ * @author Roman Levenstein <romixlev@gmail.com>
+ *
+ * @param <V>
+ */
+public class Some<V> extends Option<V>{
+    final V value;
+    public Some(V v) {
+        value = v;
+    }
+    
+    public V get() {
+        return value;
+    }
+    
+    public boolean nonEmpty () {
+        return value != null;
+    }
+}
diff --git a/third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/BasicNode.java b/third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/BasicNode.java
new file mode 100644 (file)
index 0000000..ed5cdbe
--- /dev/null
@@ -0,0 +1,20 @@
+/*                     __                                               *\
+**     ________ ___   / /  ___     Scala API                            **
+**    / __/ __// _ | / /  / _ |    (c) 2003-2012, LAMP/EPFL             **
+**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
+** /____/\___/_/ |_/____/_/ | |                                         **
+**                          |/                                          **
+\*                                                                      */
+
+package com.romix.scala.collection.concurrent;
+
+
+
+
+
+
+abstract class BasicNode {
+    
+    public abstract String string(int lev);
+    
+}
\ No newline at end of file
diff --git a/third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/CNodeBase.java b/third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/CNodeBase.java
new file mode 100644 (file)
index 0000000..33b21c5
--- /dev/null
@@ -0,0 +1,35 @@
+/*                     __                                               *\
+**     ________ ___   / /  ___     Scala API                            **
+**    / __/ __// _ | / /  / _ |    (c) 2003-2012, LAMP/EPFL             **
+**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
+** /____/\___/_/ |_/____/_/ | |                                         **
+**                          |/                                          **
+\*                                                                      */
+
+package com.romix.scala.collection.concurrent;
+
+
+
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+
+
+
+abstract class CNodeBase<K, V> extends MainNode<K, V> {
+    
+    public static final AtomicIntegerFieldUpdater<CNodeBase> updater = AtomicIntegerFieldUpdater.newUpdater(CNodeBase.class, "csize");
+    
+    public volatile int csize = -1;
+    
+    public boolean CAS_SIZE(int oldval, int nval) {
+    return updater.compareAndSet(this, oldval, nval);
+    }
+    
+    public void WRITE_SIZE(int nval) {
+    updater.set(this, nval);
+    }
+    
+    public int READ_SIZE() {
+    return updater.get(this);
+    }
+    
+}
\ No newline at end of file
diff --git a/third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/Gen.java b/third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/Gen.java
new file mode 100644 (file)
index 0000000..15aba43
--- /dev/null
@@ -0,0 +1,17 @@
+/*                     __                                               *\
+**     ________ ___   / /  ___     Scala API                            **
+**    / __/ __// _ | / /  / _ |    (c) 2003-2012, LAMP/EPFL             **
+**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
+** /____/\___/_/ |_/____/_/ | |                                         **
+**                          |/                                          **
+\*                                                                      */
+
+package com.romix.scala.collection.concurrent;
+
+
+
+
+
+
+final class Gen {
+}
\ No newline at end of file
diff --git a/third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/INodeBase.java b/third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/INodeBase.java
new file mode 100644 (file)
index 0000000..11a3b34
--- /dev/null
@@ -0,0 +1,35 @@
+/*                     __                                               *\
+**     ________ ___   / /  ___     Scala API                            **
+**    / __/ __// _ | / /  / _ |    (c) 2003-2012, LAMP/EPFL             **
+**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
+** /____/\___/_/ |_/____/_/ | |                                         **
+**                          |/                                          **
+\*                                                                      */
+
+package com.romix.scala.collection.concurrent;
+
+
+
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+
+
+abstract class INodeBase<K, V> extends BasicNode {
+    
+    public static final AtomicReferenceFieldUpdater<INodeBase, MainNode> updater = AtomicReferenceFieldUpdater.newUpdater(INodeBase.class, MainNode.class, "mainnode");
+    
+    public static final Object RESTART = new Object();
+    
+    public volatile MainNode<K, V> mainnode = null;
+    
+    public final Gen gen;
+    
+    public INodeBase(Gen generation) {
+    gen = generation;
+    }
+    
+    public BasicNode prev() {
+    return null;
+    }
+    
+}
\ No newline at end of file
diff --git a/third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/ListMap.java b/third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/ListMap.java
new file mode 100644 (file)
index 0000000..bef25f7
--- /dev/null
@@ -0,0 +1,230 @@
+package com.romix.scala.collection.concurrent;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import com.romix.scala.Option;
+
+/**
+ * Mimic immutable ListMap in Scala
+ *  
+ * @author Roman Levenstein <romixlev@gmail.com>
+ *
+ * @param <V>
+ */
+abstract class ListMap<K,V> {
+
+    ListMap<K,V> next;
+    
+    static <K,V> ListMap<K, V> map(K k, V v, ListMap<K, V> tail){
+        return new Node<K, V> (k, v, tail);
+    }
+
+    static <K,V> ListMap<K, V> map(K k, V v){
+        return new Node<K, V> (k, v, null);
+    }
+
+    static <K,V> ListMap<K, V> map(K k1, V v1, K k2, V v2){
+        return new Node<K, V> (k1, v1, new Node<K,V>(k2,v2, null));
+    }
+    
+    public abstract int size ();
+
+    public abstract boolean isEmpty() ;
+    
+    abstract public boolean contains(K k, V v);
+
+    abstract public boolean contains(K key);
+
+    abstract public Option<V> get (K key);
+
+    abstract public ListMap<K, V> add (K key, V value);
+
+    abstract public ListMap<K, V> remove (K key);
+    
+    abstract public Iterator<Map.Entry<K, V>> iterator();
+
+    
+    static class EmptyListMap<K,V> extends ListMap<K, V> {
+        public ListMap<K,V> add (K key, V value) {
+            return ListMap.map(key, value, null);
+        }
+        
+        public boolean contains(K k, V v) {
+            return false;
+        }
+        
+        public boolean contains(K k) {
+            return false;
+        }
+        
+        public ListMap<K,V> remove(K key) {
+            return this;
+        }
+
+        @Override
+        public int size () {
+            return 0;
+        }
+
+        @Override
+        public boolean isEmpty () {
+            return true;
+        }
+
+        @Override
+        public Option<V> get (K key) {
+            return Option.makeOption (null);
+        }
+
+        @Override
+        public Iterator<Entry<K, V>> iterator () {
+            return new EmptyListMapIterator<K, V> ();
+        } 
+        
+        static class EmptyListMapIterator<K,V> implements Iterator<Entry<K, V>> {
+
+            @Override
+            public boolean hasNext () {
+                return false;
+            }
+
+            @Override
+            public Entry<K, V> next () {
+                return null;
+            }
+
+            @Override
+            public void remove () {
+                throw new RuntimeException("Operation not supported");
+            }
+            
+        }
+    }
+    
+    static class Node<K,V> extends ListMap<K, V> {
+        final K k;
+        final V v;
+        
+        Node(K k, V v, ListMap<K,V> next) {
+            this.k = k;
+            this.v = v;
+            this.next = next;
+        }
+        
+        public ListMap<K,V> add (K key, V value) {
+            return ListMap.map(key, value, remove (key));
+        }
+                
+        public boolean contains(K k, V v) {
+            if(k.equals (this.k) && v.equals (this.v))
+                return true;
+            else if(next != null) 
+                return next.contains (k, v);
+            return false;
+        }
+        
+        public boolean contains(K k) {
+            if(k.equals (this.k))
+                return true;
+            else if(next != null) 
+                return next.contains (k);
+            return false;
+        }
+        
+        public ListMap<K,V> remove(K key) {
+            if(!contains(key))
+                return this;
+            else
+                return remove0(key);
+        }
+        
+        private ListMap<K, V> remove0 (K key) {
+            ListMap<K, V> n = this;
+            ListMap<K, V> newN = null;
+            ListMap<K, V> lastN = null;
+            while (n != null) {
+                if(n instanceof EmptyListMap) {
+                    newN.next = n;
+                    break;
+                }
+                Node<K, V> nn = (Node<K, V>)n;
+                if (key.equals (nn.k)) {
+                    n = n.next;
+                    continue;
+                } else {
+                    if (newN != null) {
+                        lastN.next = ListMap.map (nn.k, nn.v, null);
+                        lastN = lastN.next;
+                    } else {
+                        newN = ListMap.map (nn.k, nn.v, null);
+                        lastN = newN;
+                    }
+                }
+                n = n.next;
+            }
+            return newN;
+        }
+
+        @Override
+        public int size () {
+            if(next == null)
+                return 1;
+            else
+                return 1+next.size ();
+        }
+
+        @Override
+        public boolean isEmpty () {
+            return false;
+        }
+
+        @Override
+        public Option<V> get (K key) {
+            if(key.equals (k))
+                return Option.makeOption (v);
+            if(next != null)
+                return next.get (key);
+            return Option.makeOption (null);
+        }
+
+
+        @Override
+        public Iterator<Entry<K, V>> iterator () {
+            return new NodeIterator<K, V> (this);
+        }
+
+        static class NodeIterator<K,V> implements Iterator<Entry<K, V>> {
+            ListMap<K, V> n;
+
+            public NodeIterator (Node<K, V> n) {
+                this.n = n;
+            }
+
+            @Override
+            public boolean hasNext () {
+//                return n!= null && n.next != null && !(n.next instanceof EmptyListMap);
+                return n!= null && !(n instanceof EmptyListMap);
+            }
+
+            @Override
+            public Entry<K, V> next () {
+                if (n instanceof Node) {
+                    Node<K, V> nn = (Node<K, V>) n;
+                    Pair<K, V> res = new Pair<K, V> (nn.k, nn.v);
+                    n = n.next;
+                    return res;
+                } else {
+                    return null;
+                }
+            }
+
+            @Override
+            public void remove () {
+                throw new RuntimeException("Operation not supported");
+            }
+            
+        }
+    }
+}
diff --git a/third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/MainNode.java b/third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/MainNode.java
new file mode 100644 (file)
index 0000000..8238725
--- /dev/null
@@ -0,0 +1,36 @@
+/*                     __                                               *\
+ **     ________ ___   / /  ___     Scala API                            **
+ **    / __/ __// _ | / /  / _ |    (c) 2003-2012, LAMP/EPFL             **
+ **  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
+ ** /____/\___/_/ |_/____/_/ | |                                         **
+ **                          |/                                          **
+\*                                                                      */
+
+package com.romix.scala.collection.concurrent;
+
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+abstract class MainNode<K, V> extends BasicNode {
+
+    public static final AtomicReferenceFieldUpdater<MainNode, MainNode> updater = AtomicReferenceFieldUpdater.newUpdater (MainNode.class, MainNode.class, "prev");
+
+    public volatile MainNode<K, V> prev = null;
+
+    public abstract int cachedSize (Object ct);
+
+    public boolean CAS_PREV (MainNode<K, V> oldval, MainNode<K, V> nval) {
+        return updater.compareAndSet (this, oldval, nval);
+    }
+
+    public void WRITE_PREV (MainNode<K, V> nval) {
+        updater.set (this, nval);
+    }
+
+    // do we need this? unclear in the javadocs...
+    // apparently not - volatile reads are supposed to be safe
+    // regardless of whether there are concurrent ARFU updates
+    public MainNode<K, V> READ_PREV () {
+        return updater.get (this);
+    }
+
+}
diff --git a/third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/Pair.java b/third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/Pair.java
new file mode 100644 (file)
index 0000000..18c262d
--- /dev/null
@@ -0,0 +1,40 @@
+package com.romix.scala.collection.concurrent;
+
+import java.util.Map;
+
+/***
+ * Helper class simulating a tuple of 2 elements in Scala
+ * 
+ * @author Roman Levenstein <romixlev@gmail.com>
+ *
+ * @param <K>
+ * @param <V>
+ */
+public class Pair<K, V> implements Map.Entry<K, V> {
+
+    final K k;
+    final V v;
+
+    Pair (K k, V v) {
+        this.k = k;
+        this.v = v;
+    }
+
+    @Override
+    public K getKey () {
+        // TODO Auto-generated method stub
+        return k;
+    }
+
+    @Override
+    public V getValue () {
+        // TODO Auto-generated method stub
+        return v;
+    }
+
+    @Override
+    public V setValue (V value) {
+        throw new RuntimeException ("Operation not supported");
+    }
+
+}
diff --git a/third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/TrieMap.java b/third-party/triemap/src/main/java/com/romix/scala/collection/concurrent/TrieMap.java
new file mode 100644 (file)
index 0000000..8f92922
--- /dev/null
@@ -0,0 +1,1840 @@
+package com.romix.scala.collection.concurrent;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import com.romix.scala.None;
+import com.romix.scala.Option;
+import com.romix.scala.Some;
+
+/***
+ * This is a port of Scala's TrieMap class from the Scala Collections library.
+ * 
+ * @author Roman Levenstein <romixlev@gmail.com>
+ *
+ * @param <K>
+ * @param <V>
+ */
+@SuppressWarnings({"unchecked", "rawtypes", "unused"})
+public class TrieMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K,V>, Serializable {
+    private static final AtomicReferenceFieldUpdater<TrieMap, Object> ROOT_UPDATER = AtomicReferenceFieldUpdater.newUpdater(TrieMap.class, Object.class, "root");
+    private static final long serialVersionUID = 1L;
+    private static final Field READONLY_FIELD;
+    private static final TrieMap EMPTY = new TrieMap();
+
+    static {
+        final Field f;
+        try {
+            f = TrieMap.class.getDeclaredField("readOnly");
+        } catch (NoSuchFieldException e) {
+            throw new ExceptionInInitializerError(e);
+        } catch (SecurityException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+        f.setAccessible(true);
+        READONLY_FIELD = f;
+    }
+
+    /**
+     * EntrySet
+     */
+    private transient final EntrySet entrySet = new EntrySet ();
+    
+    public static <K,V> TrieMap<K,V> empty () {
+        return EMPTY;
+    }
+
+    // static class MangledHashing<K> extends Hashing<K> {
+    // int hash(K k) {
+    // return util.hashing.byteswap32(k);
+    // }
+    // }
+
+    static class INode<K, V> extends INodeBase<K, V> {
+        static final Object KEY_PRESENT = new Object ();
+        static final Object KEY_ABSENT = new Object ();
+
+        static <K,V> INode<K,V> newRootNode () {
+            Gen gen = new Gen ();
+            CNode<K, V> cn = new CNode<K, V> (0, new BasicNode[] {}, gen);
+            return new INode<K,V>(cn, gen);
+        }
+
+        public INode (MainNode<K, V> bn, Gen g) {
+            super (g);
+            WRITE (bn);
+        }
+
+        public INode (Gen g) {
+            this (null, g);
+        }
+
+        final void WRITE (final MainNode<K, V> nval) {
+            INodeBase.updater.set (this, nval);
+        }
+
+        final boolean CAS (final MainNode<K, V> old, final MainNode<K, V> n) {
+            return INodeBase.updater.compareAndSet (this, old, n);
+        }
+
+        final MainNode<K, V> gcasRead (final TrieMap<K, V> ct) {
+            return GCAS_READ (ct);
+        }
+
+        final MainNode<K, V> GCAS_READ (TrieMap<K, V> ct) {
+            MainNode<K, V> m = /* READ */mainnode;
+            MainNode<K, V> prevval = /* READ */m.prev;
+            if (prevval == null)
+                return m;
+            else
+                return GCAS_Complete (m, ct);
+        }
+
+        private MainNode<K, V> GCAS_Complete (MainNode<K, V> m, final TrieMap<K, V> ct) {
+            while (true) {
+                if (m == null)
+                    return null;
+                else {
+                    // complete the GCAS
+                    MainNode<K, V> prev = /* READ */m.prev;
+                    INode<K, V> ctr = ct.readRoot (true);
+
+                    if (prev == null)
+                        return m;
+
+                    if (prev instanceof FailedNode) {
+                        // try to commit to previous value
+                        FailedNode<K, V> fn = (FailedNode<K, V>) prev;
+                        if (CAS (m, fn.prev))
+                            return fn.prev;
+                        else {
+                            // Tailrec
+                            // return GCAS_Complete (/* READ */mainnode, ct);
+                            m = /* READ */mainnode;
+                            continue;
+                        }
+                    } else if (prev instanceof MainNode) {
+                        // Assume that you've read the root from the generation
+                        // G.
+                        // Assume that the snapshot algorithm is correct.
+                        // ==> you can only reach nodes in generations <= G.
+                        // ==> `gen` is <= G.
+                        // We know that `ctr.gen` is >= G.
+                        // ==> if `ctr.gen` = `gen` then they are both equal to
+                        // G.
+                        // ==> otherwise, we know that either `ctr.gen` > G,
+                        // `gen` <
+                        // G,
+                        // or both
+                        if ((ctr.gen == gen) && ct.nonReadOnly ()) {
+                            // try to commit
+                            if (m.CAS_PREV (prev, null))
+                                return m;
+                            else {
+                                // return GCAS_Complete (m, ct);
+                                // tailrec
+                                continue;
+                            }
+                        } else {
+                            // try to abort
+                            m.CAS_PREV (prev, new FailedNode<K, V> (prev));
+                            return GCAS_Complete (/* READ */mainnode, ct);
+                        }
+                    }
+                }
+                throw new RuntimeException ("Should not happen");
+            }
+        }
+
+        final boolean GCAS (final MainNode<K, V> old, final MainNode<K, V> n, final TrieMap<K, V> ct) {
+            n.WRITE_PREV (old);
+            if (CAS (old, n)) {
+                GCAS_Complete (n, ct);
+                return /* READ */n.prev == null;
+            } else
+                return false;
+        }
+
+        private boolean equal (final K k1, final K k2, final TrieMap<K, V> ct) {
+            return ct.equality ().equiv (k1, k2);
+        }
+
+        private INode<K, V> inode (final MainNode<K, V> cn) {
+            INode<K, V> nin = new INode<K, V> (gen);
+            nin.WRITE (cn);
+            return nin;
+        }
+
+        final INode<K, V> copyToGen (final Gen ngen, final TrieMap<K, V> ct) {
+            INode<K, V> nin = new INode<K, V> (ngen);
+            MainNode<K, V> main = GCAS_READ (ct);
+            nin.WRITE (main);
+            return nin;
+        }
+
+        /**
+         * Inserts a key value pair, overwriting the old pair if the keys match.
+         * 
+         * @return true if successful, false otherwise
+         */
+        final boolean rec_insert (final K k, final V v, final int hc, final int lev, final INode<K, V> parent, final Gen startgen, final TrieMap<K, V> ct) {
+            while (true) {
+                MainNode<K, V> m = GCAS_READ (ct); // use -Yinline!
+
+                if (m instanceof CNode) {
+                    // 1) a multiway node
+                    CNode<K, V> cn = (CNode<K, V>) m;
+                    int idx = (hc >>> lev) & 0x1f;
+                    int flag = 1 << idx;
+                    int bmp = cn.bitmap;
+                    int mask = flag - 1;
+                    int pos = Integer.bitCount (bmp & mask);
+                    if ((bmp & flag) != 0) {
+                        // 1a) insert below
+                        BasicNode cnAtPos = cn.array [pos];
+                        if (cnAtPos instanceof INode) {
+                            INode<K, V> in = (INode<K, V>) cnAtPos;
+                            if (startgen == in.gen)
+                                return in.rec_insert (k, v, hc, lev + 5, this, startgen, ct);
+                            else {
+                                if (GCAS (cn, cn.renewed (startgen, ct), ct)) {
+                                    // return rec_insert (k, v, hc, lev, parent,
+                                    // startgen, ct);
+                                    // tailrec
+                                    continue;
+                                } else
+                                    return false;
+                            }
+                        } else if (cnAtPos instanceof SNode) {
+                            SNode<K, V> sn = (SNode<K, V>) cnAtPos;
+                            if (sn.hc == hc && equal ((K) sn.k, k, ct))
+                                return GCAS (cn, cn.updatedAt (pos, new SNode<K, V> (k, v, hc), gen), ct);
+                            else {
+                                CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed (gen, ct);
+                                MainNode<K, V> nn = rn.updatedAt (pos, inode (CNode.dual (sn, sn.hc, new SNode (k, v, hc), hc, lev + 5, gen)), gen);
+                                return GCAS (cn, nn, ct);
+                            }
+                        }
+                    } else {
+                        CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed (gen, ct);
+                        MainNode<K, V> ncnode = rn.insertedAt (pos, flag, new SNode<K, V> (k, v, hc), gen);
+                        return GCAS (cn, ncnode, ct);
+                    }
+                } else if (m instanceof TNode) {
+                    clean (parent, ct, lev - 5);
+                    return false;
+                } else if (m instanceof LNode) {
+                    LNode<K, V> ln = (LNode<K, V>) m;
+                    MainNode<K, V> nn = ln.inserted (k, v);
+                    return GCAS (ln, nn, ct);
+                }
+
+                throw new RuntimeException ("Should not happen");
+            }
+        }
+
+        /**
+         * Inserts a new key value pair, given that a specific condition is met.
+         * 
+         * @param cond
+         *            null - don't care if the key was there
+         *            KEY_ABSENT - key wasn't there
+         *            KEY_PRESENT - key was there 
+         *            other value `v` - key must be bound to `v`
+         * @return null if unsuccessful, Option[V] otherwise (indicating
+         *         previous value bound to the key)
+         */
+        final Option<V> rec_insertif (final K k, final V v, final int hc, final Object cond, final int lev, final INode<K, V> parent, final Gen startgen, final TrieMap<K, V> ct) {
+            while (true) {
+                MainNode<K, V> m = GCAS_READ (ct); // use -Yinline!
+
+                if (m instanceof CNode) {
+                    // 1) a multiway node
+                    CNode<K, V> cn = (CNode<K, V>) m;
+                    int idx = (hc >>> lev) & 0x1f;
+                    int flag = 1 << idx;
+                    int bmp = cn.bitmap;
+                    int mask = flag - 1;
+                    int pos = Integer.bitCount (bmp & mask);
+
+                    if ((bmp & flag) != 0) {
+                        // 1a) insert below
+                        BasicNode cnAtPos = cn.array [pos];
+                        if (cnAtPos instanceof INode) {
+                            INode<K, V> in = (INode<K, V>) cnAtPos;
+                            if (startgen == in.gen)
+                                return in.rec_insertif (k, v, hc, cond, lev + 5, this, startgen, ct);
+                            else {
+                                if (GCAS (cn, cn.renewed (startgen, ct), ct)) {
+                                    // return rec_insertif (k, v, hc, cond, lev,
+                                    // parent, startgen, ct);
+                                    // tailrec
+                                    continue;
+                                } else
+                                    return null;
+                            }
+                        } else if (cnAtPos instanceof SNode) {
+                            SNode<K, V> sn = (SNode<K, V>) cnAtPos;
+                            if (cond == null) {
+                                if (sn.hc == hc && equal (sn.k, k, ct)) {
+                                    if (GCAS (cn, cn.updatedAt (pos, new SNode<K, V> (k, v, hc), gen), ct))
+                                        return Option.makeOption(sn.v);
+                                    else
+                                        return null;
+                                } else {
+                                    CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed (gen, ct);
+                                    MainNode<K, V> nn = rn.updatedAt (pos, inode (CNode.dual (sn, sn.hc, new SNode (k, v, hc), hc, lev + 5, gen)), gen);
+                                    if (GCAS (cn, nn, ct))
+                                        return Option.makeOption(); // None;
+                                    else
+                                        return null;
+                                }
+
+                            } else if (cond == INode.KEY_ABSENT) {
+                                if (sn.hc == hc && equal (sn.k, k, ct))
+                                    return Option.makeOption(sn.v);
+                                else {
+                                    CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed (gen, ct);
+                                    MainNode<K, V> nn = rn.updatedAt (pos, inode (CNode.dual (sn, sn.hc, new SNode (k, v, hc), hc, lev + 5, gen)), gen);
+                                    if (GCAS (cn, nn, ct))
+                                        return Option.makeOption (); // None
+                                    else
+                                        return null;
+                                }
+                            } else if (cond == INode.KEY_PRESENT) {
+                                if (sn.hc == hc && equal (sn.k, k, ct)) {
+                                    if (GCAS (cn, cn.updatedAt (pos, new SNode<K, V> (k, v, hc), gen), ct))
+                                        return Option.makeOption (sn.v);
+                                    else
+                                        return null;
+
+                                } else
+                                    return Option.makeOption ();// None;
+                            } else {
+                                if (sn.hc == hc && equal (sn.k, k, ct) && sn.v == cond) {
+                                    if (GCAS (cn, cn.updatedAt (pos, new SNode<K, V> (k, v, hc), gen), ct))
+                                        return Option.makeOption (sn.v);
+                                    else
+                                        return null;
+                                } else
+                                    return Option.makeOption (); // None
+                            }
+
+                        }
+                    } else if (cond == null || cond == INode.KEY_ABSENT) {
+                        CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed (gen, ct);
+                        CNode<K, V> ncnode = rn.insertedAt (pos, flag, new SNode<K, V> (k, v, hc), gen);
+                        if (GCAS (cn, ncnode, ct))
+                            return Option.makeOption ();// None
+                        else
+                            return null;
+                    } else if (cond == INode.KEY_PRESENT) {
+                        return Option.makeOption ();// None;
+                    } else 
+                        return Option.makeOption (); // None
+                } else if (m instanceof TNode) {
+                    clean (parent, ct, lev - 5);
+                    return null;
+                } else if (m instanceof LNode) {
+                    // 3) an l-node
+                    LNode<K, V> ln = (LNode<K, V>) m;
+                    if (cond == null) {
+                        Option<V> optv = ln.get (k);
+                        if (insertln (ln, k, v, ct))
+                            return optv;
+                        else
+                            return null;
+                    } else if (cond == INode.KEY_ABSENT) {
+                        Option<V> t = ln.get (k);
+                        if (t == null) {
+                            if (insertln (ln, k, v, ct))
+                                return Option.makeOption ();// None
+                            else
+                                return null;
+                        } else
+                            return t;
+                    } else if (cond == INode.KEY_PRESENT) {
+                        Option<V> t = ln.get (k);
+                        if (t != null) {
+                            if (insertln (ln, k, v, ct))
+                                return t;
+                            else
+                                return null;
+                        } else
+                            return null; // None
+                    } else {
+                        Option<V> t = ln.get (k);
+                        if (t != null) {
+                            if (((Some<V>) t).get () == cond) {
+                                if (insertln (ln, k, v, ct))
+                                    return new Some<V> ((V) cond);
+                                else
+                                    return null;
+
+                            } else
+                                return Option.makeOption ();
+                        }
+                    }
+                }
+
+//                throw new RuntimeException ("Should not happen");
+            }
+        }
+
+        final boolean insertln (final LNode<K, V> ln, final K k, final V v, final TrieMap<K, V> ct) {
+            LNode<K, V> nn = ln.inserted (k, v);
+            return GCAS (ln, nn, ct);
+        }
+
+        /**
+         * Looks up the value associated with the key.
+         * 
+         * @return null if no value has been found, RESTART if the operation
+         *         wasn't successful, or any other value otherwise
+         */
+        final Object rec_lookup (final K k, final int hc, int lev, INode<K, V> parent, final Gen startgen, final TrieMap<K, V> ct) {
+            while (true) {
+                MainNode<K, V> m = GCAS_READ (ct); // use -Yinline!
+
+                if (m instanceof CNode) {
+                    // 1) a multinode
+                    final CNode<K, V> cn = (CNode<K, V>) m;
+                    int idx = (hc >>> lev) & 0x1f;
+                    int flag = 1 << idx;
+                    int bmp = cn.bitmap;
+                    if ((bmp & flag) == 0)
+                        return null; // 1a) bitmap shows no binding
+                    else { // 1b) bitmap contains a value - descend
+                        int pos = (bmp == 0xffffffff) ? idx : Integer.bitCount (bmp & (flag - 1));
+                        final BasicNode sub = cn.array [pos];
+                        if (sub instanceof INode) {
+                            INode<K, V> in = (INode<K, V>) sub;
+                            if (ct.isReadOnly () || (startgen == ((INodeBase<K, V>) sub).gen))
+                                return in.rec_lookup (k, hc, lev + 5, this, startgen, ct);
+                            else {
+                                if (GCAS (cn, cn.renewed (startgen, ct), ct)) {
+                                    // return rec_lookup (k, hc, lev, parent,
+                                    // startgen, ct);
+                                    // Tailrec
+                                    continue;
+                                } else
+                                    return RESTART; // used to be throw
+                                                    // RestartException
+                            }
+                        } else if (sub instanceof SNode) {
+                            // 2) singleton node
+                            SNode<K, V> sn = (SNode<K, V>) sub;
+                            if (((SNode) sub).hc == hc && equal (sn.k, k, ct))
+                                return sn.v;
+                            else
+                                return null;
+                        }
+                    }
+                } else if (m instanceof TNode) {
+                    // 3) non-live node
+                    return cleanReadOnly ((TNode<K, V>) m, lev, parent, ct, k, hc);
+                } else if (m instanceof LNode) {
+                    // 5) an l-node
+                    Option<V> tmp = ((LNode<K, V>) m).get (k);
+                    return (tmp instanceof Option) ? ((Option<V>) tmp) : null;
+                }
+
+                throw new RuntimeException ("Should not happen");
+            }
+        }
+
+        private Object cleanReadOnly (final TNode<K, V> tn, final int lev, final INode<K, V> parent, final TrieMap<K, V> ct, K k, int hc) {
+            if (ct.nonReadOnly ()) {
+                clean (parent, ct, lev - 5);
+                return RESTART; // used to be throw RestartException
+            } else {
+                if (tn.hc == hc && equal(tn.k, k, ct))
+                    return tn.v;
+                else
+                    return null;
+            }
+        }
+
+        /**
+         * Removes the key associated with the given value.
+         * 
+         * @param v
+         *            if null, will remove the key irregardless of the value;
+         *            otherwise removes only if binding contains that exact key
+         *            and value
+         * @return null if not successful, an Option[V] indicating the previous
+         *         value otherwise
+         */
+        final Option<V> rec_remove (K k, V v, int hc, int lev, final INode<K, V> parent, final Gen startgen, final TrieMap<K, V> ct) {
+            MainNode<K, V> m = GCAS_READ (ct); // use -Yinline!
+
+            if (m instanceof CNode) {
+                CNode<K, V> cn = (CNode<K, V>) m;
+                int idx = (hc >>> lev) & 0x1f;
+                int bmp = cn.bitmap;
+                int flag = 1 << idx;
+                if ((bmp & flag) == 0)
+                    return Option.makeOption ();
+                else {
+                    int pos = Integer.bitCount (bmp & (flag - 1));
+                    BasicNode sub = cn.array [pos];
+                    Option<V> res = null;
+                    if (sub instanceof INode) {
+                        INode<K, V> in = (INode<K, V>) sub;
+                        if (startgen == in.gen)
+                            res = in.rec_remove (k, v, hc, lev + 5, this, startgen, ct);
+                        else {
+                            if (GCAS (cn, cn.renewed (startgen, ct), ct))
+                                res = rec_remove (k, v, hc, lev, parent, startgen, ct);
+                            else
+                                res = null;
+                        }
+
+                    } else if (sub instanceof SNode) {
+                        SNode<K, V> sn = (SNode<K, V>) sub;
+                        if (sn.hc == hc && equal (sn.k, k, ct) && (v == null || v.equals(sn.v))) {
+                            MainNode<K, V> ncn = cn.removedAt (pos, flag, gen).toContracted (lev);
+                            if (GCAS (cn, ncn, ct))
+                                res = Option.makeOption (sn.v);
+                            else
+                                res = null;
+                        } else
+                            res = Option.makeOption ();
+                    }
+
+                    if (res instanceof None || (res == null))
+                        return res;
+                    else {
+                        if (parent != null) { // never tomb at root
+                            MainNode<K, V> n = GCAS_READ (ct);
+                            if (n instanceof TNode)
+                                cleanParent (n, parent, ct, hc, lev, startgen);
+                        }
+
+                        return res;
+                    }
+                }
+            } else if (m instanceof TNode) {
+                clean (parent, ct, lev - 5);
+                return null;
+            } else if (m instanceof LNode) {
+                LNode<K, V> ln = (LNode<K, V>) m;
+                if (v == null) {
+                    Option<V> optv = ln.get (k);
+                    MainNode<K, V> nn = ln.removed (k, ct);
+                    if (GCAS (ln, nn, ct))
+                        return optv;
+                    else
+                        return null;
+                } else {
+                    Option<V> tmp = ln.get (k);
+                    if (tmp instanceof Some) {
+                        Some<V> tmp1 = (Some<V>) tmp;
+                        if (tmp1.get () == v) {
+                            MainNode<K, V> nn = ln.removed (k, ct);
+                            if (GCAS (ln, nn, ct))
+                                return tmp;
+                            else
+                                return null;
+                        }
+                    }
+                }
+            }
+            throw new RuntimeException ("Should not happen");
+        }
+
+        void cleanParent (final Object nonlive, final INode<K, V> parent, final TrieMap<K, V> ct, final int hc, final int lev, final Gen startgen) {
+            while (true) {
+                MainNode<K, V> pm = parent.GCAS_READ (ct);
+                if (pm instanceof CNode) {
+                    CNode<K, V> cn = (CNode<K, V>) pm;
+                    int idx = (hc >>> (lev - 5)) & 0x1f;
+                    int bmp = cn.bitmap;
+                    int flag = 1 << idx;
+                    if ((bmp & flag) == 0) {
+                    } // somebody already removed this i-node, we're done
+                    else {
+                        int pos = Integer.bitCount (bmp & (flag - 1));
+                        BasicNode sub = cn.array [pos];
+                        if (sub == this) {
+                            if (nonlive instanceof TNode) {
+                                TNode<K, V> tn = (TNode<K, V>) nonlive;
+                                MainNode<K, V> ncn = cn.updatedAt (pos, tn.copyUntombed (), gen).toContracted (lev - 5);
+                                if (!parent.GCAS (cn, ncn, ct))
+                                    if (ct.readRoot ().gen == startgen) {
+                                        // cleanParent (nonlive, parent, ct, hc,
+                                        // lev, startgen);
+                                        // tailrec
+                                        continue;
+                                    }
+                            }
+                        }
+                    }
+                } else {
+                    // parent is no longer a cnode, we're done
+                }
+                break;
+            }
+        }
+
+        private void clean (final INode<K, V> nd, final TrieMap<K, V> ct, int lev) {
+            MainNode<K, V> m = nd.GCAS_READ (ct);
+            if (m instanceof CNode) {
+                CNode<K, V> cn = (CNode<K, V>) m;
+                nd.GCAS (cn, cn.toCompressed (ct, lev, gen), ct);
+            }
+        }
+
+        final boolean isNullInode (final TrieMap<K, V> ct) {
+            return GCAS_READ (ct) == null;
+        }
+
+        final int cachedSize (final TrieMap<K, V> ct) {
+            MainNode<K, V> m = GCAS_READ (ct);
+            return m.cachedSize (ct);
+        }
+
+        // /* this is a quiescent method! */
+        // def string(lev: Int) = "%sINode -> %s".format("  " * lev, mainnode
+        // match {
+        // case null => "<null>"
+        // case tn: TNode[_, _] => "TNode(%s, %s, %d, !)".format(tn.k, tn.v,
+        // tn.hc)
+        // case cn: CNode[_, _] => cn.string(lev)
+        // case ln: LNode[_, _] => ln.string(lev)
+        // case x => "<elem: %s>".format(x)
+        // })
+
+        public String string (int lev) {
+            return "INode";
+        }
+
+    }
+
+    private static final class FailedNode<K, V> extends MainNode<K, V> {
+        MainNode<K, V> p;
+
+        FailedNode (final MainNode<K, V> p) {
+            this.p = p;
+            WRITE_PREV (p);
+        }
+
+        public String string (int lev) {
+            throw new UnsupportedOperationException ();
+        }
+
+        public int cachedSize (Object ct) {
+            throw new UnsupportedOperationException ();
+        }
+
+        public String toString () {
+            return String.format ("FailedNode(%s)", p);
+        }
+    }
+
+    private interface KVNode<K, V> {
+        Map.Entry<K, V> kvPair ();
+    }
+
+    private static final class SNode<K, V> extends BasicNode implements KVNode<K, V> {
+        final K k;
+        final V v;
+        final int hc;
+
+        SNode (final K k, final V v, final int hc) {
+            this.k = k;
+            this.v = v;
+            this.hc = hc;
+        }
+
+        final SNode<K, V> copy() {
+            return new SNode<K, V> (k, v, hc);
+        }
+
+        final TNode<K, V> copyTombed () {
+            return new TNode<K, V> (k, v, hc);
+        }
+
+        final SNode<K, V> copyUntombed () {
+            return new SNode<K, V> (k, v, hc);
+        }
+
+        final public Map.Entry<K, V> kvPair () {
+            return new Pair<K, V> (k, v);
+        }
+
+        final public String string (int lev) {
+            // ("  " * lev) + "SNode(%s, %s, %x)".format(k, v, hc);
+            return "SNode";
+        }
+    }
+
+    private static final class TNode<K, V> extends MainNode<K, V> implements KVNode<K, V> {
+        final K k;
+        final V v;
+        final int hc;
+
+        TNode (final K k, final V v, final int hc) {
+            this.k = k;
+            this.v = v;
+            this.hc = hc;
+        }
+
+        final TNode<K, V> copy () {
+            return new TNode<K, V> (k, v, hc);
+        }
+
+        final TNode<K, V> copyTombed () {
+            return new TNode<K, V> (k, v, hc);
+        }
+
+        final SNode<K, V> copyUntombed () {
+            return new SNode<K, V> (k, v, hc);
+        }
+
+        final public Pair<K, V> kvPair () {
+            return new Pair<K, V> (k, v);
+        }
+
+        final public int cachedSize (Object ct) {
+            return 1;
+        }
+
+        final public String string (int lev) {
+            // ("  " * lev) + "TNode(%s, %s, %x, !)".format(k, v, hc);
+            return "TNode";
+        }
+    }
+
+    private final static class LNode<K, V> extends MainNode<K, V> {
+        final ListMap<K, V> listmap;
+
+        public LNode (final ListMap<K, V> listmap) {
+            this.listmap = listmap;
+        }
+
+        public LNode(K k, V v) {
+            this (ListMap.map (k, v));
+        }
+
+        public LNode (K k1, V v1, K k2, V v2) {
+            this (ListMap.map (k1, v1, k2, v2));
+        }
+
+        LNode<K, V> inserted (K k, V v) {
+            return new LNode<K, V> (listmap.add (k, v));
+        }
+
+        MainNode<K, V> removed (K k, final TrieMap<K, V> ct) {
+            ListMap<K, V> updmap = listmap.remove (k);
+            if (updmap.size () > 1)
+                return new LNode<K, V> (updmap);
+            else {
+                Entry<K, V> kv = updmap.iterator ().next ();
+                // create it tombed so that it gets compressed on subsequent
+                // accesses
+                return new TNode<K, V> (kv.getKey (), kv.getValue (), ct.computeHash (kv.getKey ()));
+            }
+        }
+
+        Option<V> get (K k) {
+            return listmap.get (k);
+        }
+
+        public int cachedSize (Object ct) {
+            return listmap.size ();
+        }
+
+        public String string (int lev) {
+            // (" " * lev) + "LNode(%s)".format(listmap.mkString(", "))
+            return "LNode";
+        }
+    }
+
+    private static final class CNode<K, V> extends CNodeBase<K, V> {
+
+        final int bitmap;
+        final BasicNode[] array;
+        final Gen gen;
+
+        CNode (final int bitmap, final BasicNode[] array, final Gen gen) {
+            this.bitmap = bitmap;
+            this.array = array;
+            this.gen = gen;
+        }
+
+        // this should only be called from within read-only snapshots
+        final public int cachedSize (Object ct) {
+            int currsz = READ_SIZE ();
+            if (currsz != -1)
+                return currsz;
+            else {
+                int sz = computeSize ((TrieMap<K, V>) ct);
+                while (READ_SIZE () == -1)
+                    CAS_SIZE (-1, sz);
+                return READ_SIZE ();
+            }
+        }
+
+        // lends itself towards being parallelizable by choosing
+        // a random starting offset in the array
+        // => if there are concurrent size computations, they start
+        // at different positions, so they are more likely to
+        // to be independent
+        private int computeSize (final TrieMap<K, V> ct) {
+            int i = 0;
+            int sz = 0;
+            // final int offset = (array.length > 0) ?
+            // // util.Random.nextInt(array.length) /* <-- benchmarks show that
+            // // this causes observable contention */
+            // scala.concurrent.forkjoin.ThreadLocalRandom.current.nextInt (0,
+            // array.length)
+            // : 0;
+
+            final int offset = 0;
+            while (i < array.length) {
+                int pos = (i + offset) % array.length;
+                BasicNode elem = array [pos];
+                if (elem instanceof SNode)
+                    sz += 1;
+                else if (elem instanceof INode)
+                    sz += ((INode<K, V>) elem).cachedSize (ct);
+                i += 1;
+            }
+            return sz;
+        }
+
+        final CNode<K, V> updatedAt (int pos, final BasicNode nn, final Gen gen) {
+            int len = array.length;
+            BasicNode[] narr = new BasicNode[len];
+            System.arraycopy (array, 0, narr, 0, len);
+            narr [pos] = nn;
+            return new CNode<K, V> (bitmap, narr, gen);
+        }
+
+        final CNode<K, V> removedAt (int pos, int flag, final Gen gen) {
+            BasicNode[] arr = array;
+            int len = arr.length;
+            BasicNode[] narr = new BasicNode[len - 1];
+            System.arraycopy (arr, 0, narr, 0, pos);
+            System.arraycopy (arr, pos + 1, narr, pos, len - pos - 1);
+            return new CNode<K, V> (bitmap ^ flag, narr, gen);
+        }
+
+        final CNode<K, V> insertedAt (int pos, int flag, final BasicNode nn, final Gen gen) {
+            int len = array.length;
+            int bmp = bitmap;
+            BasicNode[] narr = new BasicNode[len + 1];
+            System.arraycopy (array, 0, narr, 0, pos);
+            narr [pos] = nn;
+            System.arraycopy (array, pos, narr, pos + 1, len - pos);
+            return new CNode<K, V> (bmp | flag, narr, gen);
+        }
+
+        /**
+         * Returns a copy of this cnode such that all the i-nodes below it are
+         * copied to the specified generation `ngen`.
+         */
+        final CNode<K, V> renewed (final Gen ngen, final TrieMap<K, V> ct) {
+            int i = 0;
+            BasicNode[] arr = array;
+            int len = arr.length;
+            BasicNode[] narr = new BasicNode[len];
+            while (i < len) {
+                BasicNode elem = arr [i];
+                if (elem instanceof INode) {
+                    INode<K, V> in = (INode<K, V>) elem;
+                    narr [i] = in.copyToGen (ngen, ct);
+                } else if (elem instanceof BasicNode)
+                    narr [i] = elem;
+                i += 1;
+            }
+            return new CNode<K, V> (bitmap, narr, ngen);
+        }
+
+        private BasicNode resurrect (final INode<K, V> inode, final Object inodemain) {
+            if (inodemain instanceof TNode) {
+                TNode<K, V> tn = (TNode<K, V>) inodemain;
+                return tn.copyUntombed ();
+            } else
+                return inode;
+        }
+
+        final MainNode<K, V> toContracted (int lev) {
+            if (array.length == 1 && lev > 0) {
+                if (array [0] instanceof SNode) {
+                    SNode<K, V> sn = (SNode<K, V>) array [0];
+                    return sn.copyTombed ();
+                } else
+                    return this;
+
+            } else
+                return this;
+        }
+
+        // - if the branching factor is 1 for this CNode, and the child
+        // is a tombed SNode, returns its tombed version
+        // - otherwise, if there is at least one non-null node below,
+        // returns the version of this node with at least some null-inodes
+        // removed (those existing when the op began)
+        // - if there are only null-i-nodes below, returns null
+        final MainNode<K, V> toCompressed (final TrieMap<K, V> ct, int lev, Gen gen) {
+            int bmp = bitmap;
+            int i = 0;
+            BasicNode[] arr = array;
+            BasicNode[] tmparray = new BasicNode[arr.length];
+            while (i < arr.length) { // construct new bitmap
+                BasicNode sub = arr [i];
+                if (sub instanceof INode) {
+                    INode<K, V> in = (INode<K, V>) sub;
+                    MainNode<K, V> inodemain = in.gcasRead (ct);
+                    assert (inodemain != null);
+                    tmparray [i] = resurrect (in, inodemain);
+                } else if (sub instanceof SNode) {
+                    tmparray [i] = sub;
+                }
+                i += 1;
+            }
+
+            return new CNode<K, V> (bmp, tmparray, gen).toContracted (lev);
+        }
+
+        public String string (int lev) {
+            // "CNode %x\n%s".format(bitmap, array.map(_.string(lev +
+            // 1)).mkString("\n"));
+            return "CNode";
+        }
+
+        /*
+         * quiescently consistent - don't call concurrently to anything
+         * involving a GCAS!!
+         */
+        // protected Seq<K,V> collectElems() {
+        // array flatMap {
+        // case sn: SNode[K, V] => Some(sn.kvPair)
+        // case in: INode[K, V] => in.mainnode match {
+        // case tn: TNode[K, V] => Some(tn.kvPair)
+        // case ln: LNode[K, V] => ln.listmap.toList
+        // case cn: CNode[K, V] => cn.collectElems
+        // }
+        // }
+        // }
+
+        // protected Seq<String> collectLocalElems() {
+        // // array flatMap {
+        // // case sn: SNode[K, V] => Some(sn.kvPair._2.toString)
+        // // case in: INode[K, V] => Some(in.toString.drop(14) + "(" + in.gen +
+        // ")")
+        // // }
+        // return null;
+        // }
+
+        public String toString () {
+            // val elems = collectLocalElems
+            // "CNode(sz: %d; %s)".format(elems.size,
+            // elems.sorted.mkString(", "))
+            return "CNode";
+        }
+
+        static <K, V> MainNode<K,V> dual (final SNode<K, V> x, int xhc, final SNode<K, V> y, int yhc, int lev, Gen gen) {
+            if (lev < 35) {
+                int xidx = (xhc >>> lev) & 0x1f;
+                int yidx = (yhc >>> lev) & 0x1f;
+                int bmp = (1 << xidx) | (1 << yidx);
+
+                if (xidx == yidx) {
+                    INode<K, V> subinode = new INode<K, V> (gen);// (TrieMap.inodeupdater)
+                    subinode.mainnode = dual (x, xhc, y, yhc, lev + 5, gen);
+                    return new CNode<K, V> (bmp, new BasicNode[] { subinode }, gen);
+                } else {
+                    if (xidx < yidx)
+                        return new CNode<K, V> (bmp, new BasicNode[] { x, y }, gen);
+                    else
+                        return new CNode<K, V> (bmp, new BasicNode[] { y, x }, gen);
+                }
+            } else {
+                return new LNode<K, V> (x.k, x.v, y.k, y.v);
+            }
+        }
+
+    }
+
+    private static class RDCSS_Descriptor<K, V> {
+        INode<K, V> old;
+        MainNode<K, V> expectedmain;
+        INode<K, V> nv;
+        volatile boolean committed = false;
+
+        public RDCSS_Descriptor (final INode<K, V> old, final MainNode<K, V> expectedmain, final INode<K, V> nv) {
+            this.old = old;
+            this.expectedmain = expectedmain;
+            this.nv = nv;
+        }
+
+    }
+
+    private static class Equiv<K> implements Serializable {
+        private static final long serialVersionUID = 1L;
+
+        public boolean equiv (K k1, K k2) {
+            return k1.equals (k2);
+        }
+
+        static Equiv universal = new Equiv ();
+    }
+
+    private static interface Hashing<K> extends Serializable {
+        public int hash (K k);
+    }
+
+    static class Default<K> implements Hashing<K> {
+        private static final long serialVersionUID = 1L;
+
+        public int hash (K k) {
+            int h = k.hashCode ();
+            // This function ensures that hashCodes that differ only by
+            // constant multiples at each bit position have a bounded
+            // number of collisions (approximately 8 at default load factor).
+            h ^= (h >>> 20) ^ (h >>> 12);
+            h ^= (h >>> 7) ^ (h >>> 4);
+            return h;
+        }
+    }
+
+    private final Hashing<K> hashingobj;
+    private final Equiv<K> equalityobj;
+
+    Hashing<K> hashing () {
+        return hashingobj;
+    }
+
+    Equiv<K> equality () {
+        return equalityobj;
+    }
+
+    private transient volatile Object root;
+    private final transient boolean readOnly;
+
+    TrieMap (final Hashing<K> hashf, final Equiv<K> ef, final boolean readOnly) {
+        this.hashingobj = hashf;
+        this.equalityobj = ef;
+        this.readOnly = readOnly;
+    }
+
+    TrieMap (final Object r, final Hashing<K> hashf, final Equiv<K> ef, boolean readOnly) {
+        this(hashf, ef, readOnly);
+        this.root = r;
+    }
+
+    public TrieMap (final Hashing<K> hashf, final Equiv<K> ef) {
+        this(INode.newRootNode(), hashf, ef, false);
+    }
+
+    public TrieMap () {
+        this (new Default<K> (), Equiv.universal);
+    }
+
+    /* internal methods */
+
+    // private void writeObject(java.io.ObjectOutputStream out) {
+    // out.writeObject(hashf);
+    // out.writeObject(ef);
+    //
+    // Iterator it = iterator();
+    // while (it.hasNext) {
+    // val (k, v) = it.next();
+    // out.writeObject(k);
+    // out.writeObject(v);
+    // }
+    // out.writeObject(TrieMapSerializationEnd);
+    // }
+    //
+    // private TrieMap readObject(java.io.ObjectInputStream in) {
+    // root = INode.newRootNode();
+    // rootupdater = AtomicReferenceFieldUpdater.newUpdater(TrieMap.class,
+    // Object.class, "root");
+    //
+    // hashingobj = in.readObject();
+    // equalityobj = in.readObject();
+    //
+    // Object obj = null;
+    // do {
+    // obj = in.readObject();
+    // if (obj != TrieMapSerializationEnd) {
+    // K k = (K)obj;
+    // V = (V)in.readObject();
+    // update(k, v);
+    // }
+    // } while (obj != TrieMapSerializationEnd);
+    // }
+
+    final boolean CAS_ROOT (Object ov, Object nv) {
+        if (isReadOnly()) {
+            throw new IllegalStateException("Attempted to modify a read-only snapshot");
+        }
+        return ROOT_UPDATER.compareAndSet (this, ov, nv);
+    }
+
+    // FIXME: abort = false by default
+    final INode<K, V> readRoot (boolean abort) {
+        return RDCSS_READ_ROOT (abort);
+    }
+
+    final INode<K, V> readRoot () {
+        return RDCSS_READ_ROOT (false);
+    }
+
+    final INode<K, V> RDCSS_READ_ROOT () {
+        return RDCSS_READ_ROOT (false);
+    }
+
+    final INode<K, V> RDCSS_READ_ROOT (boolean abort) {
+        Object r = /* READ */root;
+        if (r instanceof INode)
+            return (INode<K, V>) r;
+        else if (r instanceof RDCSS_Descriptor) {
+            return RDCSS_Complete (abort);
+        }
+        throw new RuntimeException ("Should not happen");
+    }
+
+    private final INode<K, V> RDCSS_Complete (final boolean abort) {
+        while (true) {
+            Object v = /* READ */root;
+            if (v instanceof INode)
+                return (INode<K, V>) v;
+            else if (v instanceof RDCSS_Descriptor) {
+                RDCSS_Descriptor<K, V> desc = (RDCSS_Descriptor<K, V>) v;
+                INode<K, V> ov = desc.old;
+                MainNode<K, V> exp = desc.expectedmain;
+                INode<K, V> nv = desc.nv;
+
+                if (abort) {
+                    if (CAS_ROOT (desc, ov))
+                        return ov;
+                    else {
+                        // return RDCSS_Complete (abort);
+                        // tailrec
+                        continue;
+                    }
+                } else {
+                    MainNode<K, V> oldmain = ov.gcasRead (this);
+                    if (oldmain == exp) {
+                        if (CAS_ROOT (desc, nv)) {
+                            desc.committed = true;
+                            return nv;
+                        } else {
+                            // return RDCSS_Complete (abort);
+                            // tailrec
+                            continue;
+                        }
+                    } else {
+                        if (CAS_ROOT (desc, ov))
+                            return ov;
+                        else {
+                            // return RDCSS_Complete (abort);
+                            // tailrec
+                            continue;
+
+                        }
+                    }
+                }
+            }
+
+            throw new RuntimeException ("Should not happen");
+        }
+    }
+
+    private boolean RDCSS_ROOT (final INode<K, V> ov, final MainNode<K, V> expectedmain, final INode<K, V> nv) {
+        RDCSS_Descriptor<K, V> desc = new RDCSS_Descriptor<K, V> (ov, expectedmain, nv);
+        if (CAS_ROOT (ov, desc)) {
+            RDCSS_Complete (false);
+            return /* READ */desc.committed;
+        } else
+            return false;
+    }
+
+    private void inserthc (final K k, final int hc, final V v) {
+        while (true) {
+            INode<K, V> r = RDCSS_READ_ROOT ();
+            if (!r.rec_insert (k, v, hc, 0, null, r.gen, this)) {
+                // inserthc (k, hc, v);
+                // tailrec
+                continue;
+            }
+            break;
+        }
+    }
+
+    private Option<V> insertifhc (final K k, final int hc, final V v, final Object cond) {
+        while (true) {
+            INode<K, V> r = RDCSS_READ_ROOT ();
+
+            Option<V> ret = r.rec_insertif (k, v, hc, cond, 0, null, r.gen, this);
+            if (ret == null) {
+                // return insertifhc (k, hc, v, cond);
+                // tailrec
+                continue;
+            } else
+                return ret;
+        }
+    }
+
+    private Object lookuphc (final K k, final int hc) {
+        while (true) {
+            INode<K, V> r = RDCSS_READ_ROOT ();
+            Object res = r.rec_lookup (k, hc, 0, null, r.gen, this);
+            if (res == INodeBase.RESTART) {
+                // return lookuphc (k, hc);
+                // tailrec
+                continue;
+            } else
+                return res;
+        }
+    }
+
+    private Option<V> removehc (final K k, final V v, final int hc) {
+        while (true) {
+            INode<K, V> r = RDCSS_READ_ROOT ();
+            Option<V> res = r.rec_remove (k, v, hc, 0, null, r.gen, this);
+            if (res != null)
+                return res;
+            else {
+                // return removehc (k, v, hc);
+                // tailrec
+                continue;
+            }
+        }
+    }
+
+    /**
+     * Ensure this instance is read-write, throw UnsupportedOperationException
+     * otherwise. Used by Map-type methods for quick check.
+     */
+    private void ensureReadWrite() {
+        if (isReadOnly()) {
+            throw new UnsupportedOperationException("Attempted to modify a read-only view");
+        }
+    }
+
+    public String string () {
+        // RDCSS_READ_ROOT().string(0);
+        return "Root";
+    }
+
+    /* public methods */
+
+    // public Seq<V> seq() {
+    // return this;
+    // }
+
+    // override def par = new ParTrieMap(this)
+
+    // static TrieMap empty() {
+    // return new TrieMap();
+    // }
+
+    final boolean isReadOnly () {
+        return readOnly;
+    }
+
+    final boolean nonReadOnly () {
+        return !readOnly;
+    }
+
+    /**
+     * Returns a snapshot of this TrieMap. This operation is lock-free and
+     * linearizable.
+     * 
+     * The snapshot is lazily updated - the first time some branch in the
+     * snapshot or this TrieMap are accessed, they are rewritten. This means
+     * that the work of rebuilding both the snapshot and this TrieMap is
+     * distributed across all the threads doing updates or accesses subsequent
+     * to the snapshot creation.
+     */
+
+    final public TrieMap<K, V> snapshot () {
+        while (true) {
+            INode<K, V> r = RDCSS_READ_ROOT ();
+            final MainNode<K, V> expmain = r.gcasRead (this);
+            if (RDCSS_ROOT (r, expmain, r.copyToGen (new Gen (), this)))
+                return new TrieMap<K, V> (r.copyToGen (new Gen (), this), hashing (), equality (), readOnly);
+            else {
+                // return snapshot ();
+                // tailrec
+                continue;
+            }
+        }
+    }
+
+    /**
+     * Returns a read-only snapshot of this TrieMap. This operation is lock-free
+     * and linearizable.
+     * 
+     * The snapshot is lazily updated - the first time some branch of this
+     * TrieMap are accessed, it is rewritten. The work of creating the snapshot
+     * is thus distributed across subsequent updates and accesses on this
+     * TrieMap by all threads. Note that the snapshot itself is never rewritten
+     * unlike when calling the `snapshot` method, but the obtained snapshot
+     * cannot be modified.
+     * 
+     * This method is used by other methods such as `size` and `iterator`.
+     */
+    final public TrieMap<K, V> readOnlySnapshot () {
+        // Is it a snapshot of a read-only snapshot?
+        if(!nonReadOnly ())
+            return this;
+        
+        while (true) {
+            INode<K, V> r = RDCSS_READ_ROOT ();
+            MainNode<K, V> expmain = r.gcasRead (this);
+            if (RDCSS_ROOT (r, expmain, r.copyToGen (new Gen (), this)))
+                return new TrieMap<K, V> (r, hashing (), equality (), true);
+            else {
+                // return readOnlySnapshot ();
+                continue;
+            }
+        }
+    }
+
+    final public void clear () {
+        while (true) {
+            INode<K, V> r = RDCSS_READ_ROOT ();
+            if (!RDCSS_ROOT (r, r.gcasRead (this), INode.<K, V>newRootNode ())) {
+                continue;
+            }else{
+                return;
+            }
+        }
+    }
+
+    // @inline
+    int computeHash (K k) {
+        return hashingobj.hash (k);
+    }
+
+    final V lookup (K k) {
+        int hc = computeHash (k);
+//        return (V) lookuphc (k, hc);
+        Object o = lookuphc (k, hc);
+        if(o instanceof Some) {
+            return ((Some<V>)o).get ();
+        } else if(o instanceof None) 
+            return null;
+        else
+            return (V)o;
+    }
+
+    final V apply (K k) {
+        int hc = computeHash (k);
+        Object res = lookuphc (k, hc);
+        if (res == null)
+            throw new NoSuchElementException ();
+        else
+            return (V) res;
+    }
+
+//    final public Option<V> get (K k) {
+//        int hc = computeHash (k);
+//        return Option.makeOption ((V)lookuphc (k, hc));
+//    }
+
+    final public V get (Object k) {
+        return lookup((K)k);
+    }
+    
+    final public Option<V> putOpt(Object key, Object value) {
+        int hc = computeHash ((K)key);
+        return insertifhc ((K)key, hc, (V)value, null);
+    }
+
+    @Override
+    final public V put (Object key, Object value) {
+        ensureReadWrite();
+        int hc = computeHash ((K)key);
+        Option<V> ov = insertifhc ((K)key, hc, (V)value, null);
+        if(ov instanceof Some) {
+            Some<V> sv = (Some<V>)ov;
+            return sv.get ();
+        } else 
+            return null;
+    }
+    
+    final public void update (K k, V v) {
+        int hc = computeHash (k);
+        inserthc (k, hc, v);
+    }
+
+    final public TrieMap<K, V> add (K k, V v) {
+        update (k, v);
+        return this;
+    }
+
+    final Option<V> removeOpt (K k) {
+        int hc = computeHash (k);
+        return removehc (k, (V) null, hc);
+    }
+
+    @Override
+    final public V remove (Object k) {
+        ensureReadWrite();
+        int hc = computeHash ((K)k);
+        Option<V> ov = removehc ((K)k, (V) null, hc);
+        if(ov instanceof Some) {
+            Some<V> sv = (Some<V>)ov;
+            return sv.get();
+        } else 
+            return null;
+    }
+    
+//    final public TrieMap<K, V> remove (Object k) {
+//        removeOpt ((K)k);
+//        return this;
+//    }
+
+    final public Option<V> putIfAbsentOpt (K k, V v) {
+        int hc = computeHash (k);
+        return insertifhc (k, hc, v, INode.KEY_ABSENT);
+    }
+
+    @Override
+    final public V putIfAbsent (Object k, Object v) {
+        ensureReadWrite();
+        int hc = computeHash ((K)k);
+        Option<V> ov = insertifhc ((K)k, hc, (V)v, INode.KEY_ABSENT);
+        if(ov instanceof Some) {
+            Some<V> sv = (Some<V>)ov;
+            return sv.get();
+        } else 
+            return null;
+    }
+    
+    @Override
+    public boolean remove (Object k, Object v) {
+        ensureReadWrite();
+        int hc = computeHash ((K)k);
+        return removehc ((K)k, (V)v, hc).nonEmpty ();
+    }
+
+    @Override
+    public boolean replace (K k, V oldvalue, V newvalue) {
+        ensureReadWrite();
+        int hc = computeHash (k);
+        return insertifhc (k, hc, newvalue, (Object) oldvalue).nonEmpty ();
+    }
+
+    public Option<V> replaceOpt (K k, V v) {
+        int hc = computeHash (k);
+        return insertifhc (k, hc, v, INode.KEY_PRESENT);
+    }
+
+    @Override
+    public V replace (Object k, Object v) {
+        ensureReadWrite();
+        int hc = computeHash ((K)k);
+        Option<V> ov = insertifhc ((K)k, hc, (V)v, INode.KEY_PRESENT);
+        if(ov instanceof Some) {
+            Some<V> sv = (Some<V>)ov;
+            return sv.get();
+        } else 
+            return null;
+    }
+    
+    /***
+     * Return an iterator over a TrieMap.
+     * 
+     * If this is a read-only snapshot, it would return a read-only iterator.
+     * 
+     * If it is the original TrieMap or a non-readonly snapshot, it would return
+     * an iterator that would allow for updates.
+     * 
+     * @return
+     */
+    public Iterator<Map.Entry<K, V>> iterator () {
+        if (!nonReadOnly ())
+            return readOnlySnapshot ().readOnlyIterator ();
+        else
+            return new TrieMapIterator<K, V> (0, this);
+    }
+
+    /***
+     * Return an iterator over a TrieMap.
+     * This is a read-only iterator.
+     * 
+     * @return
+     */
+    public Iterator<Map.Entry<K, V>> readOnlyIterator () {
+        if (nonReadOnly ())
+            return readOnlySnapshot ().readOnlyIterator ();
+        else
+            return new TrieMapReadOnlyIterator<K, V> (0, this);
+    }
+
+    private int cachedSize () {
+        INode<K, V> r = RDCSS_READ_ROOT ();
+        return r.cachedSize (this);
+    }
+
+    public int size () {
+        if (nonReadOnly ())
+            return readOnlySnapshot ().size ();
+        else
+            return cachedSize ();
+    }
+
+    String stringPrefix () {
+        return "TrieMap";
+    }
+
+    /***
+     * This iterator is a read-only one and does not allow for any update
+     * operations on the underlying data structure.
+     * 
+     * @param <K>
+     * @param <V>
+     */
+    private static class TrieMapReadOnlyIterator<K, V> extends TrieMapIterator<K, V> {
+        TrieMapReadOnlyIterator (int level, final TrieMap<K, V> ct, boolean mustInit) {
+            super (level, ct, mustInit);
+        }
+
+        TrieMapReadOnlyIterator (int level, TrieMap<K, V> ct) {
+            this (level, ct, true);
+        }        
+        void initialize () {
+            assert (ct.isReadOnly ());
+            super.initialize ();
+        }
+        
+        public void remove () {
+            throw new UnsupportedOperationException ("Operation not supported for read-only iterators");
+        }
+        
+        Map.Entry<K, V> nextEntry(final Map.Entry<K, V> rr) {
+            // Return non-updatable entry
+            return rr;
+        }
+    }
+    
+    private static class TrieMapIterator<K, V> implements java.util.Iterator<Map.Entry<K, V>> {
+        private int level;
+        protected TrieMap<K, V> ct;
+        private final boolean mustInit;
+        private BasicNode[][] stack = new BasicNode[7][];
+        private int[] stackpos = new int[7];
+        private int depth = -1;
+        private Iterator<Map.Entry<K, V>> subiter = null;
+        private KVNode<K, V> current = null;
+        private Map.Entry<K, V> lastReturned = null;
+
+        TrieMapIterator (int level, final TrieMap<K, V> ct, boolean mustInit) {
+            this.level = level;
+            this.ct = ct;
+            this.mustInit = mustInit;
+            if (this.mustInit)
+                initialize ();
+        }
+
+        TrieMapIterator (int level, TrieMap<K, V> ct) {
+            this (level, ct, true);
+        }
+
+
+        public boolean hasNext () {
+            return (current != null) || (subiter != null);
+        }
+
+        public Map.Entry<K, V> next () {
+            if (hasNext ()) {
+                Map.Entry<K, V> r = null;
+                if (subiter != null) {
+                    r = subiter.next ();
+                    checkSubiter ();
+                } else {
+                    r = current.kvPair ();
+                    advance ();
+                }
+                
+                lastReturned = r;
+                if(r instanceof Map.Entry) {
+                    final Map.Entry<K, V> rr = (Map.Entry<K, V>)r;
+                    return nextEntry(rr);
+                }
+                return r;
+            } else {
+                // return Iterator.empty ().next ();
+                return null;
+            }
+        }
+        
+        Map.Entry<K, V> nextEntry(final Map.Entry<K, V> rr) {
+            return new Map.Entry<K, V>() {
+                private V updated = null;
+
+                @Override
+                public K getKey () {
+                    return rr.getKey ();
+                }
+
+                @Override
+                public V getValue () {
+                    return (updated == null)?rr.getValue (): updated;
+                }
+
+                @Override
+                public V setValue (V value) {
+                    updated = value;
+                    return ct.replace (getKey (), value);
+                }
+            };            
+        }
+
+        private void readin (INode<K, V> in) {
+            MainNode<K, V> m = in.gcasRead (ct);
+            if (m instanceof CNode) {
+                CNode<K, V> cn = (CNode<K, V>) m;
+                depth += 1;
+                stack [depth] = cn.array;
+                stackpos [depth] = -1;
+                advance ();
+            } else if (m instanceof TNode) {
+                current = (TNode<K, V>) m;
+            } else if (m instanceof LNode) {
+                subiter = ((LNode<K, V>) m).listmap.iterator ();
+                checkSubiter ();
+            } else if (m == null) {
+                current = null;
+            }
+        }
+
+        // @inline
+        private void checkSubiter () {
+            if (!subiter.hasNext ()) {
+                subiter = null;
+                advance ();
+            }
+        }
+
+        // @inline
+        void initialize () {
+//            assert (ct.isReadOnly ());
+            INode<K, V> r = ct.RDCSS_READ_ROOT ();
+            readin (r);
+        }
+
+        void advance () {
+            if (depth >= 0) {
+                int npos = stackpos [depth] + 1;
+                if (npos < stack [depth].length) {
+                    stackpos [depth] = npos;
+                    BasicNode elem = stack [depth] [npos];
+                    if (elem instanceof SNode) {
+                        current = (SNode<K, V>) elem;
+                    } else if (elem instanceof INode) {
+                        readin ((INode<K, V>) elem);
+                    }
+                } else {
+                    depth -= 1;
+                    advance ();
+                }
+            } else
+                current = null;
+        }
+
+        protected TrieMapIterator<K, V> newIterator (int _lev, TrieMap<K, V> _ct, boolean _mustInit) {
+            return new TrieMapIterator<K, V> (_lev, _ct, _mustInit);
+        }
+
+        protected void dupTo (TrieMapIterator<K, V> it) {
+            it.level = this.level;
+            it.ct = this.ct;
+            it.depth = this.depth;
+            it.current = this.current;
+
+            // these need a deep copy
+            System.arraycopy (this.stack, 0, it.stack, 0, 7);
+            System.arraycopy (this.stackpos, 0, it.stackpos, 0, 7);
+
+            // this one needs to be evaluated
+            if (this.subiter == null)
+                it.subiter = null;
+            else {
+                List<Map.Entry<K, V>> lst = toList (this.subiter);
+                this.subiter = lst.iterator ();
+                it.subiter = lst.iterator ();
+            }
+        }
+
+        // /** Returns a sequence of iterators over subsets of this iterator.
+        // * It's used to ease the implementation of splitters for a parallel
+        // version of the TrieMap.
+        // */
+        // protected def subdivide(): Seq[Iterator[(K, V)]] = if (subiter ne
+        // null) {
+        // // the case where an LNode is being iterated
+        // val it = subiter
+        // subiter = null
+        // advance()
+        // this.level += 1
+        // Seq(it, this)
+        // } else if (depth == -1) {
+        // this.level += 1
+        // Seq(this)
+        // } else {
+        // var d = 0
+        // while (d <= depth) {
+        // val rem = stack(d).length - 1 - stackpos(d)
+        // if (rem > 0) {
+        // val (arr1, arr2) = stack(d).drop(stackpos(d) + 1).splitAt(rem / 2)
+        // stack(d) = arr1
+        // stackpos(d) = -1
+        // val it = newIterator(level + 1, ct, false)
+        // it.stack(0) = arr2
+        // it.stackpos(0) = -1
+        // it.depth = 0
+        // it.advance() // <-- fix it
+        // this.level += 1
+        // return Seq(this, it)
+        // }
+        // d += 1
+        // }
+        // this.level += 1
+        // Seq(this)
+        // }
+
+        private List<Entry<K, V>> toList (Iterator<Entry<K, V>> it) {
+            ArrayList<Entry<K, V>> list = new ArrayList<Map.Entry<K, V>> ();
+            while (it.hasNext ()) {
+                list.add (it.next ());
+            }
+            return list;
+        }
+
+        void printDebug () {
+            System.out.println ("ctrie iterator");
+            System.out.println (Arrays.toString (stackpos));
+            System.out.println ("depth: " + depth);
+            System.out.println ("curr.: " + current);
+            // System.out.println(stack.mkString("\n"));
+        }
+
+        @Override
+        public void remove () {
+            if (lastReturned != null) {
+                ct.remove (lastReturned.getKey ());
+                lastReturned = null;
+            } else 
+                throw new IllegalStateException();
+        }
+
+    }
+
+    /** Only used for ctrie serialization. */
+    // @SerialVersionUID(0L - 7237891413820527142L)
+    private static long TrieMapSerializationEnd = 0L - 7237891413820527142L;
+
+
+    public boolean containsKey (Object key) {
+        return lookup ((K) key) != null;
+    }
+
+
+    @Override
+    public Set<Map.Entry<K, V>> entrySet () {
+        return entrySet;
+    }
+
+    /***
+     * Support for EntrySet operations required by the Map interface
+     *
+     */
+    final class EntrySet extends AbstractSet<Map.Entry<K, V>> {
+        
+        @Override
+        public Iterator<Map.Entry<K, V>> iterator () {
+            return TrieMap.this.iterator ();
+        }
+
+        @Override
+        public final boolean contains (final Object o) {
+            if (!(o instanceof Map.Entry)) {
+                return false;
+            }
+            final Map.Entry<K, V> e = (Map.Entry<K, V>) o;
+            final K k = e.getKey ();
+            final V v = lookup (k);
+            return v != null;
+        }
+
+        @Override
+        public final boolean remove (final Object o) {
+            if (!(o instanceof Map.Entry)) {
+                return false;
+            }
+            final Map.Entry<K, V> e = (Map.Entry<K, V>) o;
+            final K k = e.getKey ();
+            return null != TrieMap.this.remove (k);
+        }
+
+        @Override
+        public final int size () {
+            int size = 0;
+            for (final Iterator<?> i = iterator (); i.hasNext (); i.next ()) {
+                size++;
+            }
+            return size;
+        }
+
+        @Override
+        public final void clear () {
+            TrieMap.this.clear ();
+        }
+    }
+
+    private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
+        inputStream.defaultReadObject();
+        this.root = INode.newRootNode();
+
+        final boolean ro = inputStream.readBoolean();
+        final int size = inputStream.readInt();
+        for (int i = 0; i < size; ++i) {
+            final K key = (K)inputStream.readObject();
+            final V value = (V)inputStream.readObject();
+            add(key, value);
+        }
+
+        // Propagate the read-only bit
+        try {
+            READONLY_FIELD.setBoolean(this, ro);
+        } catch (IllegalAccessException e) {
+            throw new IOException("Failed to set read-only flag", e);
+        }
+    }
+
+    private void writeObject(ObjectOutputStream outputStream) throws IOException {
+        outputStream.defaultWriteObject();
+
+        final Map<K, V> ro = readOnlySnapshot();
+        outputStream.writeBoolean(isReadOnly());
+        outputStream.writeInt(ro.size());
+
+        for (Entry<K, V> e : ro.entrySet()) {
+            outputStream.writeObject(e.getKey());
+            outputStream.writeObject(e.getValue());
+        }
+    }
+}
diff --git a/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestCNodeFlagCollision.java b/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestCNodeFlagCollision.java
new file mode 100644 (file)
index 0000000..a08a685
--- /dev/null
@@ -0,0 +1,35 @@
+package com.romix.scala.collection.concurrent;\r
+\r
+import java.util.Map;\r
+\r
+import org.junit.Test;\r
+\r
+public class TestCNodeFlagCollision {\r
+    @Test\r
+    public void testCNodeFlagCollision () {\r
+        final Map<Object, Object> map = new TrieMap<Object, Object> ();\r
+        final Integer z15169 = Integer.valueOf (15169);\r
+        final Integer z28336 = Integer.valueOf (28336);\r
+        \r
+        TestHelper.assertTrue (null == map.get (z15169));\r
+        TestHelper.assertTrue (null == map.get (z28336));\r
+        \r
+        map.put (z15169, z15169);\r
+        TestHelper.assertTrue (null != map.get (z15169));\r
+        TestHelper.assertTrue (null == map.get (z28336));\r
+        \r
+        map.put (z28336, z28336);\r
+        TestHelper.assertTrue (null != map.get (z15169));\r
+        TestHelper.assertTrue (null != map.get (z28336));\r
+        \r
+        map.remove (z15169);\r
+        \r
+        TestHelper.assertTrue (null == map.get (z15169));\r
+        TestHelper.assertTrue (null != map.get (z28336));\r
+        \r
+        map.remove (z28336);\r
+        \r
+        TestHelper.assertTrue (null == map.get (z15169));\r
+        TestHelper.assertTrue (null == map.get (z28336));\r
+    }\r
+}\r
diff --git a/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestCNodeInsertionIncorrectOrder.java b/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestCNodeInsertionIncorrectOrder.java
new file mode 100644 (file)
index 0000000..9d36de3
--- /dev/null
@@ -0,0 +1,21 @@
+package com.romix.scala.collection.concurrent;\r
+\r
+import java.util.Map;\r
+\r
+import org.junit.Test;\r
+\r
+public class TestCNodeInsertionIncorrectOrder {\r
+\r
+    @Test\r
+    public void testCNodeInsertionIncorrectOrder () {\r
+        final Map<Object, Object> map = new TrieMap<Object, Object> ();\r
+        final Integer z3884 = Integer.valueOf (3884);\r
+        final Integer z4266 = Integer.valueOf (4266);\r
+        map.put (z3884, z3884);\r
+        TestHelper.assertTrue (null != map.get (z3884));\r
+        \r
+        map.put (z4266, z4266);\r
+        TestHelper.assertTrue (null != map.get (z3884));\r
+        TestHelper.assertTrue (null != map.get (z4266));\r
+    }\r
+}\r
diff --git a/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestConcurrentMapPutIfAbsent.java b/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestConcurrentMapPutIfAbsent.java
new file mode 100644 (file)
index 0000000..afdadcd
--- /dev/null
@@ -0,0 +1,19 @@
+package com.romix.scala.collection.concurrent;\r
+\r
+import java.util.concurrent.ConcurrentMap;\r
+\r
+import org.junit.Test;\r
+\r
+public class TestConcurrentMapPutIfAbsent {\r
+    private static final int COUNT = 50*1000;\r
+\r
+    @Test\r
+    public void testConcurrentMapPutIfAbsent () {\r
+        final ConcurrentMap<Object, Object> map = new TrieMap<Object, Object> ();\r
+        \r
+        for (int i = 0; i < COUNT; i++) {\r
+            TestHelper.assertTrue (null == map.putIfAbsent (i, i));\r
+            TestHelper.assertTrue (Integer.valueOf (i).equals (map.putIfAbsent (i, i)));\r
+        }\r
+    }\r
+}\r
diff --git a/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestConcurrentMapRemove.java b/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestConcurrentMapRemove.java
new file mode 100644 (file)
index 0000000..6796784
--- /dev/null
@@ -0,0 +1,24 @@
+package com.romix.scala.collection.concurrent;\r
+\r
+import java.util.concurrent.ConcurrentMap;\r
+\r
+import org.junit.Test;\r
+\r
+public class TestConcurrentMapRemove {\r
+    private static final int COUNT = 50*1000;\r
+\r
+    @Test\r
+    public void testConcurrentMapRemove () {\r
+        final ConcurrentMap<Object, Object> map = new TrieMap<Object, Object> ();\r
+        \r
+        for (int i = 128; i < COUNT; i++) {\r
+            TestHelper.assertFalse (map.remove (i, i));\r
+            TestHelper.assertTrue (null == map.put (i, i));\r
+            TestHelper.assertFalse (map.remove (i, "lol"));\r
+            TestHelper.assertTrue (map.containsKey (i));\r
+            TestHelper.assertTrue (map.remove (i, i));\r
+            TestHelper.assertFalse (map.containsKey (i));\r
+            TestHelper.assertTrue (null == map.put (i, i));\r
+        }\r
+    }\r
+}\r
diff --git a/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestConcurrentMapReplace.java b/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestConcurrentMapReplace.java
new file mode 100644 (file)
index 0000000..d941aca
--- /dev/null
@@ -0,0 +1,23 @@
+package com.romix.scala.collection.concurrent;\r
+\r
+import java.util.concurrent.ConcurrentMap;\r
+\r
+import org.junit.Test;\r
+\r
+public class TestConcurrentMapReplace {\r
+    private static final int COUNT = 50*1000;\r
+\r
+    @Test\r
+    public void testConcurrentMapReplace () {\r
+        final ConcurrentMap<Object, Object> map = new TrieMap<Object, Object> ();\r
+        \r
+        for (int i = 0; i < COUNT; i++) {\r
+            TestHelper.assertTrue (null == map.replace (i, "lol"));\r
+            TestHelper.assertFalse (map.replace (i, i, "lol2"));\r
+            TestHelper.assertTrue (null == map.put (i, i));\r
+            TestHelper.assertTrue (Integer.valueOf (i).equals (map.replace (i, "lol")));\r
+            TestHelper.assertFalse (map.replace (i, i, "lol2"));\r
+            TestHelper.assertTrue (map.replace (i, "lol", i));\r
+        }\r
+    }\r
+}\r
diff --git a/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestDelete.java b/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestDelete.java
new file mode 100644 (file)
index 0000000..c374174
--- /dev/null
@@ -0,0 +1,43 @@
+package com.romix.scala.collection.concurrent;\r
+\r
+import org.junit.Test;\r
+\r
+\r
+public class TestDelete {\r
+    @Test\r
+    public void testDelete () {\r
+        final TrieMap<Object, Object> bt = new TrieMap<Object, Object> ();\r
+\r
+        for (int i = 0; i < 10000; i++) {\r
+            TestHelper.assertEquals (null, bt.put (Integer.valueOf (i), Integer.valueOf (i)));\r
+            final Object lookup = bt.lookup (Integer.valueOf (i));\r
+            TestHelper.assertEquals (Integer.valueOf (i), lookup);\r
+        }\r
+        \r
+        checkAddInsert (bt, 536);\r
+        checkAddInsert (bt, 4341);\r
+        checkAddInsert (bt, 8437);\r
+        \r
+        for (int i = 0; i < 10000; i++) {\r
+            boolean removed = null != bt.remove(Integer.valueOf (i));\r
+            TestHelper.assertEquals (Boolean.TRUE, Boolean.valueOf (removed));\r
+            final Object lookup = bt.lookup (Integer.valueOf (i));\r
+            TestHelper.assertEquals (null, lookup);\r
+        }\r
+\r
+        bt.toString ();\r
+    }\r
+\r
+    private static void checkAddInsert (final TrieMap<Object, Object> bt, int k) {\r
+        final Integer v = Integer.valueOf (k);\r
+        bt.remove (v);\r
+        Object foundV = bt.lookup (v);\r
+        TestHelper.assertEquals (null, foundV);\r
+        TestHelper.assertEquals (null, bt.put (v, v));\r
+        foundV = bt.lookup (v);\r
+        TestHelper.assertEquals (v, foundV);\r
+        \r
+        TestHelper.assertEquals (v, bt.put (v, Integer.valueOf (-1)));\r
+        TestHelper.assertEquals (Integer.valueOf (-1), bt.put (v, v));\r
+    }\r
+}\r
diff --git a/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestHashCollisions.java b/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestHashCollisions.java
new file mode 100644 (file)
index 0000000..ce89f17
--- /dev/null
@@ -0,0 +1,179 @@
+package com.romix.scala.collection.concurrent;\r
+\r
+\r
+import org.junit.Test;\r
+\r
+public class TestHashCollisions {\r
+    @Test\r
+    public void testHashCollisions () {\r
+        final TrieMap<Object, Object> bt = new TrieMap<Object, Object> ();\r
+\r
+        insertStrings (bt);\r
+        insertChars (bt);\r
+        insertInts (bt);\r
+        insertBytes (bt);\r
+        \r
+        removeStrings (bt);\r
+        removeChars (bt);\r
+        removeInts (bt);\r
+        removeBytes (bt);\r
+\r
+        insertStrings (bt);\r
+        insertInts (bt);\r
+        insertBytes (bt);\r
+        insertChars (bt);\r
+\r
+        removeBytes (bt);\r
+        removeStrings (bt);\r
+        removeChars (bt);\r
+        removeInts (bt);\r
+\r
+        insertStrings (bt);\r
+        insertInts (bt);\r
+        insertBytes (bt);\r
+        insertChars (bt);\r
+\r
+        removeStrings (bt);\r
+        removeChars (bt);\r
+        removeInts (bt);\r
+        removeBytes (bt);\r
+\r
+        insertStrings (bt);\r
+        insertInts (bt);\r
+        insertBytes (bt);\r
+        insertChars (bt);\r
+\r
+        removeChars (bt);\r
+        removeInts (bt);\r
+        removeBytes (bt);\r
+        removeStrings (bt);\r
+\r
+        insertStrings (bt);\r
+        insertInts (bt);\r
+        insertBytes (bt);\r
+        insertChars (bt);\r
+\r
+        removeInts (bt);\r
+        removeBytes (bt);\r
+        removeStrings (bt);\r
+        removeChars (bt);\r
+\r
+        System.out.println (bt);\r
+    }\r
+\r
+    private static void insertChars (final TrieMap<Object, Object> bt) {\r
+        TestHelper.assertEquals (null, bt.put ('a', 'a'));\r
+        TestHelper.assertEquals (null, bt.put ('b', 'b'));\r
+        TestHelper.assertEquals (null, bt.put ('c', 'c'));\r
+        TestHelper.assertEquals (null, bt.put ('d', 'd'));\r
+        TestHelper.assertEquals (null, bt.put ('e', 'e'));\r
+\r
+        TestHelper.assertEquals ('a', bt.put ('a', 'a'));\r
+        TestHelper.assertEquals ('b', bt.put ('b', 'b'));\r
+        TestHelper.assertEquals ('c', bt.put ('c', 'c'));\r
+        TestHelper.assertEquals ('d', bt.put ('d', 'd'));\r
+        TestHelper.assertEquals ('e', bt.put ('e', 'e'));\r
+    }\r
+\r
+    private static void insertStrings (final TrieMap<Object, Object> bt) {\r
+        TestHelper.assertEquals (null, bt.put ("a", "a"));\r
+        TestHelper.assertEquals (null, bt.put ("b", "b"));\r
+        TestHelper.assertEquals (null, bt.put ("c", "c"));\r
+        TestHelper.assertEquals (null, bt.put ("d", "d"));\r
+        TestHelper.assertEquals (null, bt.put ("e", "e"));\r
+\r
+        TestHelper.assertEquals ("a", bt.put ("a", "a"));\r
+        TestHelper.assertEquals ("b", bt.put ("b", "b"));\r
+        TestHelper.assertEquals ("c", bt.put ("c", "c"));\r
+        TestHelper.assertEquals ("d", bt.put ("d", "d"));\r
+        TestHelper.assertEquals ("e", bt.put ("e", "e"));\r
+    }\r
+\r
+    private static void insertBytes (final TrieMap<Object, Object> bt) {\r
+        for (byte i = 0; i < 128 && i >= 0; i++) {\r
+            final Byte bigB = Byte.valueOf (i);\r
+            TestHelper.assertEquals (null, bt.put (bigB, bigB));\r
+            TestHelper.assertEquals (bigB, bt.put (bigB, bigB));\r
+        }\r
+    }\r
+\r
+    private static void insertInts (final TrieMap<Object, Object> bt) {\r
+        for (int i = 0; i < 128; i++) {\r
+            final Integer bigI = Integer.valueOf (i);\r
+            TestHelper.assertEquals (null, bt.put (bigI, bigI));\r
+            TestHelper.assertEquals (bigI, bt.put (bigI, bigI));\r
+        }\r
+    }\r
+\r
+    private static void removeChars (final TrieMap<Object, Object> bt) {\r
+        TestHelper.assertTrue (null != bt.lookup ('a'));\r
+        TestHelper.assertTrue (null != bt.lookup ('b'));\r
+        TestHelper.assertTrue (null != bt.lookup ('c'));\r
+        TestHelper.assertTrue (null != bt.lookup ('d'));\r
+        TestHelper.assertTrue (null != bt.lookup ('e'));\r
+\r
+        TestHelper.assertTrue (null != bt.remove ('a'));\r
+        TestHelper.assertTrue (null != bt.remove ('b'));\r
+        TestHelper.assertTrue (null != bt.remove ('c'));\r
+        TestHelper.assertTrue (null != bt.remove ('d'));\r
+        TestHelper.assertTrue (null != bt.remove ('e'));\r
+\r
+        TestHelper.assertFalse (null != bt.remove ('a'));\r
+        TestHelper.assertFalse (null != bt.remove ('b'));\r
+        TestHelper.assertFalse (null != bt.remove ('c'));\r
+        TestHelper.assertFalse (null != bt.remove ('d'));\r
+        TestHelper.assertFalse (null != bt.remove ('e'));\r
+\r
+        TestHelper.assertTrue (null == bt.lookup ('a'));\r
+        TestHelper.assertTrue (null == bt.lookup ('b'));\r
+        TestHelper.assertTrue (null == bt.lookup ('c'));\r
+        TestHelper.assertTrue (null == bt.lookup ('d'));\r
+        TestHelper.assertTrue (null == bt.lookup ('e'));\r
+    }\r
+\r
+    private static void removeStrings (final TrieMap<Object, Object> bt) {\r
+        TestHelper.assertTrue (null != bt.lookup ("a"));\r
+        TestHelper.assertTrue (null != bt.lookup ("b"));\r
+        TestHelper.assertTrue (null != bt.lookup ("c"));\r
+        TestHelper.assertTrue (null != bt.lookup ("d"));\r
+        TestHelper.assertTrue (null != bt.lookup ("e"));\r
+\r
+        TestHelper.assertTrue (null != bt.remove ("a"));\r
+        TestHelper.assertTrue (null != bt.remove ("b"));\r
+        TestHelper.assertTrue (null != bt.remove ("c"));\r
+        TestHelper.assertTrue (null != bt.remove ("d"));\r
+        TestHelper.assertTrue (null != bt.remove ("e"));\r
+\r
+        TestHelper.assertFalse (null != bt.remove ("a"));\r
+        TestHelper.assertFalse (null != bt.remove ("b"));\r
+        TestHelper.assertFalse (null != bt.remove ("c"));\r
+        TestHelper.assertFalse (null != bt.remove ("d"));\r
+        TestHelper.assertFalse (null != bt.remove ("e"));\r
+\r
+        TestHelper.assertTrue (null == bt.lookup ("a"));\r
+        TestHelper.assertTrue (null == bt.lookup ("b"));\r
+        TestHelper.assertTrue (null == bt.lookup ("c"));\r
+        TestHelper.assertTrue (null == bt.lookup ("d"));\r
+        TestHelper.assertTrue (null == bt.lookup ("e"));\r
+    }\r
+\r
+    private static void removeInts (final TrieMap<Object, Object> bt) {\r
+        for (int i = 0; i < 128; i++) {\r
+            final Integer bigI = Integer.valueOf (i);\r
+            TestHelper.assertTrue (null != bt.lookup (bigI));\r
+            TestHelper.assertTrue (null != bt.remove (bigI));\r
+            TestHelper.assertFalse (null != bt.remove (bigI));\r
+            TestHelper.assertTrue (null == bt.lookup (bigI));\r
+        }\r
+    }\r
+\r
+    private static void removeBytes (final TrieMap<Object, Object> bt) {\r
+        for (byte i = 0; i < 128 && i >= 0; i++) {\r
+            final Byte bigB = Byte.valueOf (i);\r
+            TestHelper.assertTrue (null != bt.lookup (bigB));\r
+            TestHelper.assertTrue (null != bt.remove (bigB));\r
+            TestHelper.assertFalse (null != bt.remove (bigB));\r
+            TestHelper.assertTrue (null == bt.lookup (bigB));\r
+        }\r
+    }\r
+}\r
diff --git a/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestHashCollisionsRemove.java b/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestHashCollisionsRemove.java
new file mode 100644 (file)
index 0000000..a991eb0
--- /dev/null
@@ -0,0 +1,29 @@
+package com.romix.scala.collection.concurrent;\r
+\r
+import java.util.Map;\r
+\r
+import org.junit.Test;\r
+\r
+public class TestHashCollisionsRemove {\r
+    @Test\r
+    public void  testHashCollisionsRemove() {\r
+        final Map<Object, Object> bt = new TrieMap<Object, Object> ();\r
+        int count = 50000;\r
+        for (int j = 0; j < count; j++) {\r
+            final Object[] objects = TestMultiThreadMapIterator.getObjects (j);\r
+            for (final Object o : objects) {\r
+                bt.put (o, o);\r
+            }\r
+        }\r
+        \r
+        for (int j = 0; j < count; j++) {\r
+            final Object[] objects = TestMultiThreadMapIterator.getObjects (j);\r
+            for (final Object o : objects) {\r
+                bt.remove (o);\r
+            }\r
+        }\r
+\r
+        TestHelper.assertEquals (0, bt.size ());\r
+        TestHelper.assertTrue (bt.isEmpty ());\r
+    }\r
+}\r
diff --git a/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestHashCollisionsRemoveIterator.java b/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestHashCollisionsRemoveIterator.java
new file mode 100644 (file)
index 0000000..91783d6
--- /dev/null
@@ -0,0 +1,31 @@
+package com.romix.scala.collection.concurrent;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Iterator;\r
+import java.util.Map;\r
+import java.util.Map.Entry;\r
+\r
+import org.junit.Test;\r
+\r
+public class TestHashCollisionsRemoveIterator {\r
+    @Test\r
+    public void testHashCollisionsRemoveIterator () {\r
+        final Map<Object, Object> bt = new TrieMap<Object, Object> ();\r
+        int count = 50000;\r
+        for (int j = 0; j < count; j++) {\r
+            bt.put (Integer.valueOf (j), Integer.valueOf (j));\r
+        }\r
+        \r
+        final Collection<Object> list = new ArrayList <Object> ();\r
+        for (final Iterator<Map.Entry<Object, Object>> i = bt.entrySet ().iterator (); i.hasNext ();) {\r
+            final Entry<Object, Object> e = i.next ();\r
+            final Object key = e.getKey ();\r
+            list.add (key);\r
+            i.remove ();\r
+        }\r
+\r
+        TestHelper.assertEquals (0, bt.size ());\r
+        TestHelper.assertTrue (bt.isEmpty ());\r
+    }\r
+}\r
diff --git a/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestHelper.java b/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestHelper.java
new file mode 100644 (file)
index 0000000..26331bb
--- /dev/null
@@ -0,0 +1,27 @@
+package com.romix.scala.collection.concurrent;\r
+\r
+import org.junit.Assert;\r
+\r
+public class TestHelper {\r
+\r
+    public static void assertEquals (long expected, long found) {\r
+        Assert.assertEquals (expected, found);\r
+    }\r
+\r
+    public static void assertEquals (int expected, int found) {\r
+        Assert.assertEquals (expected, found);\r
+    }\r
+\r
+    public static void assertEquals (Object expected, Object found) {\r
+        Assert.assertEquals (expected, found);\r
+    }\r
+\r
+    public static void assertTrue (boolean found) {\r
+        Assert.assertTrue (found);\r
+    }\r
+\r
+    public static void assertFalse (boolean found) {\r
+        Assert.assertFalse (found);\r
+    }\r
+\r
+}\r
diff --git a/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestInsert.java b/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestInsert.java
new file mode 100644 (file)
index 0000000..69e0b68
--- /dev/null
@@ -0,0 +1,23 @@
+package com.romix.scala.collection.concurrent;\r
+\r
+import org.junit.Test;\r
+\r
+public class TestInsert {\r
+    @Test\r
+    public void testInsert () {\r
+        final TrieMap<Object, Object> bt = new TrieMap<Object, Object> ();\r
+        TestHelper.assertEquals (null, bt.put ("a", "a"));\r
+        TestHelper.assertEquals (null, bt.put ("b", "b"));\r
+        TestHelper.assertEquals (null, bt.put ("c", "b"));\r
+        TestHelper.assertEquals (null, bt.put ("d", "b"));\r
+        TestHelper.assertEquals (null, bt.put ("e", "b"));\r
+\r
+        for (int i = 0; i < 10000; i++) {\r
+            TestHelper.assertEquals (null, bt.put (Integer.valueOf (i), Integer.valueOf (i)));\r
+            final Object lookup = bt.lookup (Integer.valueOf (i));\r
+            TestHelper.assertEquals (Integer.valueOf (i), lookup);\r
+        }\r
+\r
+        bt.toString ();\r
+    }\r
+}\r
diff --git a/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestInstantiationSpeed.java b/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestInstantiationSpeed.java
new file mode 100644 (file)
index 0000000..1318a37
--- /dev/null
@@ -0,0 +1,39 @@
+package com.romix.scala.collection.concurrent;
+
+import org.junit.Test;
+
+public class TestInstantiationSpeed {
+    private static final int COUNT = 1000000;
+    private static final int ITERATIONS = 10;
+    private static final int WARMUP = 20;
+
+    private long runIteration() {
+        final TrieMap<?, ?>[] maps = new TrieMap<?, ?>[COUNT];
+        final long start = System.nanoTime();
+
+        for (int i = 0; i < COUNT; ++i) {
+            maps[i] = TrieMap.empty();
+        }
+
+        final long stop = System.nanoTime();
+        return stop - start;
+    }
+
+    @Test
+    public void testInstantiation() {
+
+        for (int i = 0; i < WARMUP; ++i) {
+            final long time = runIteration();
+            System.out.println(String.format("Warmup %s took %sns (%sns)", i, time, time / COUNT));
+        }
+
+        long acc = 0;
+        for (int i = 0; i < ITERATIONS; ++i) {
+            final long time = runIteration();
+            System.out.println(String.format("Iteration %s took %sns (%sns)", i, time, time / COUNT));
+            acc += time;
+        }
+
+        System.out.println("Instantiation cost " + acc / ITERATIONS / COUNT + "ns");
+    }
+}
diff --git a/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestMapIterator.java b/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestMapIterator.java
new file mode 100644 (file)
index 0000000..4815911
--- /dev/null
@@ -0,0 +1,57 @@
+package com.romix.scala.collection.concurrent;\r
+\r
+import java.util.HashSet;\r
+import java.util.Iterator;\r
+import java.util.Map;\r
+import java.util.Map.Entry;\r
+import java.util.Random;\r
+import java.util.Set;\r
+\r
+import org.junit.Test;\r
+\r
+public class TestMapIterator {\r
+    @Test\r
+    public void testMapIterator () {\r
+        for (int i = 0; i < 60 * 1000; i+= 400 + new Random ().nextInt (400)) {\r
+            System.out.println (i);\r
+            final Map<Integer, Integer> bt = new TrieMap <Integer, Integer> ();\r
+            for (int j = 0; j < i; j++) {\r
+                TestHelper.assertEquals (null, bt.put (Integer.valueOf (j), Integer.valueOf (j)));\r
+            }\r
+            int count = 0;\r
+            final Set<Integer> set = new HashSet<Integer> ();\r
+            for (final Map.Entry<Integer, Integer> e : bt.entrySet ()) {\r
+                set.add (e.getKey ());\r
+                count++;\r
+            }\r
+            for (final Integer j : set) {\r
+                TestHelper.assertTrue (bt.containsKey (j));\r
+            }\r
+            for (final Integer j : bt.keySet ()) {\r
+                TestHelper.assertTrue (set.contains (j));\r
+            }\r
+\r
+            TestHelper.assertEquals (i, count);\r
+            TestHelper.assertEquals (i, bt.size ());\r
+            \r
+            for (final Iterator<Map.Entry<Integer, Integer>> iter = bt.entrySet ().iterator (); iter.hasNext ();) {\r
+                final Entry<Integer, Integer> e = iter.next ();\r
+                TestHelper.assertTrue (e.getValue () == bt.get (e.getKey ()));\r
+                e.setValue (e.getValue () + 1);\r
+                TestHelper.assertTrue (e.getValue () == e.getKey () + 1);\r
+                TestHelper.assertTrue (e.getValue () == bt.get (e.getKey ()));\r
+                e.setValue (e.getValue () - 1);\r
+            }\r
+\r
+            for (final Iterator<Integer> iter = bt.keySet ().iterator (); iter.hasNext ();) {\r
+                final Integer k = iter.next ();\r
+                TestHelper.assertTrue (bt.containsKey (k));\r
+                iter.remove ();\r
+                TestHelper.assertFalse (bt.containsKey (k));\r
+            }\r
+            \r
+            TestHelper.assertEquals (0, bt.size ());\r
+            TestHelper.assertTrue (bt.isEmpty ());\r
+        }\r
+    }\r
+}\r
diff --git a/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestMultiThreadAddDelete.java b/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestMultiThreadAddDelete.java
new file mode 100644 (file)
index 0000000..99280c2
--- /dev/null
@@ -0,0 +1,114 @@
+package com.romix.scala.collection.concurrent;\r
+\r
+import java.util.Map;\r
+import java.util.concurrent.ExecutorService;\r
+import java.util.concurrent.Executors;\r
+import java.util.concurrent.TimeUnit;\r
+\r
+import org.junit.Test;\r
+\r
+public class TestMultiThreadAddDelete {\r
+    private static final int RETRIES = 1;\r
+    private static final int N_THREADS = 7;\r
+    private static final int COUNT =  50 * 1000;\r
+\r
+    @Test\r
+    public void testMultiThreadAddDelete () {\r
+        for (int j = 0; j < RETRIES; j++) {\r
+            final Map<Object, Object> bt = new TrieMap <Object, Object> ();\r
+            \r
+            {\r
+                final ExecutorService es = Executors.newFixedThreadPool (N_THREADS);\r
+                for (int i = 0; i < N_THREADS; i++) {\r
+                    final int threadNo = i;\r
+                    es.execute (new Runnable () {\r
+                        @Override\r
+                        public void run () {\r
+                            for (int j = 0; j < COUNT; j++) {\r
+                                if (j % N_THREADS == threadNo) {\r
+                                    bt.put (Integer.valueOf (j), Integer.valueOf (j));\r
+                                }\r
+                            }\r
+                        }\r
+                    });\r
+                }\r
+                es.shutdown ();\r
+                try {\r
+                    es.awaitTermination (3600L, TimeUnit.SECONDS);\r
+                } catch (final InterruptedException e) {\r
+                    e.printStackTrace ();\r
+                }\r
+            }\r
+            \r
+            TestHelper.assertEquals (COUNT, bt.size ());\r
+            TestHelper.assertFalse (bt.isEmpty ());\r
+            \r
+            {\r
+                final ExecutorService es = Executors.newFixedThreadPool (N_THREADS);\r
+                for (int i = 0; i < N_THREADS; i++) {\r
+                    final int threadNo = i;\r
+                    es.execute (new Runnable () {\r
+                        @Override\r
+                        public void run () {\r
+                            for (int j = 0; j < COUNT; j++) {\r
+                                if (j % N_THREADS == threadNo) {\r
+                                    bt.remove (Integer.valueOf (j));\r
+                                }\r
+                            }\r
+                        }\r
+                    });\r
+                }\r
+                es.shutdown ();\r
+                try {\r
+                    es.awaitTermination (3600L, TimeUnit.SECONDS);\r
+                } catch (final InterruptedException e) {\r
+                    e.printStackTrace ();\r
+                }\r
+            }\r
+            \r
+            \r
+            TestHelper.assertEquals (0, bt.size ());\r
+            TestHelper.assertTrue (bt.isEmpty ());\r
+            \r
+            {\r
+                final ExecutorService es = Executors.newFixedThreadPool (N_THREADS);\r
+                for (int i = 0; i < N_THREADS; i++) {\r
+                    final int threadNo = i;\r
+                    es.execute (new Runnable () {\r
+                        @Override\r
+                        public void run () {\r
+                            for (int j = 0; j < COUNT; j++) {\r
+                                if (j % N_THREADS == threadNo) {\r
+                                    try {\r
+                                        bt.put (Integer.valueOf (j), Integer.valueOf (j));\r
+                                        if (!bt.containsKey (Integer.valueOf (j))) {\r
+                                            System.out.println (j);\r
+                                        }\r
+                                        bt.remove (Integer.valueOf (j));\r
+                                        if (bt.containsKey (Integer.valueOf (j))) {\r
+                                            System.out.println (-j);\r
+                                        }\r
+                                    } catch (Throwable t) {\r
+                                        t.printStackTrace ();\r
+                                    }\r
+                                }\r
+                            }\r
+                        }\r
+                    });\r
+                }\r
+                es.shutdown ();\r
+                try {\r
+                    es.awaitTermination (3600L, TimeUnit.SECONDS);\r
+                } catch (final InterruptedException e) {\r
+                    e.printStackTrace ();\r
+                }\r
+            }\r
+            \r
+            TestHelper.assertEquals (0, bt.size ());\r
+            if (!bt.isEmpty ()) {\r
+                System.out.println ();\r
+            }\r
+            TestHelper.assertTrue (bt.isEmpty ());\r
+        }\r
+    }\r
+}\r
diff --git a/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestMultiThreadInserts.java b/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestMultiThreadInserts.java
new file mode 100644 (file)
index 0000000..2062a1c
--- /dev/null
@@ -0,0 +1,41 @@
+package com.romix.scala.collection.concurrent;\r
+\r
+import java.util.concurrent.ExecutorService;\r
+import java.util.concurrent.Executors;\r
+import java.util.concurrent.TimeUnit;\r
+\r
+import org.junit.Test;\r
+\r
+public class TestMultiThreadInserts {\r
+    @Test\r
+    public void testMultiThreadInserts () {\r
+        final int nThreads = 2;\r
+        final ExecutorService es = Executors.newFixedThreadPool (nThreads);\r
+        final TrieMap<Object, Object> bt = new TrieMap<Object, Object> ();\r
+        for (int i = 0; i < nThreads; i++) {\r
+            final int threadNo = i;\r
+            es.execute (new Runnable () {\r
+                @Override\r
+                public void run () {\r
+                    for (int j = 0; j < 500 * 1000; j++) {\r
+                        if (j % nThreads == threadNo) {\r
+                            bt.put (Integer.valueOf (j), Integer.valueOf (j));\r
+                        }\r
+                    }\r
+                }\r
+            });\r
+        }\r
+\r
+        es.shutdown ();\r
+        try {\r
+            es.awaitTermination (3600L, TimeUnit.SECONDS);\r
+        } catch (final InterruptedException e) {\r
+            e.printStackTrace ();\r
+        }\r
+        \r
+        for (int j = 0; j < 500 * 1000; j++) {\r
+            final Object lookup = bt.lookup (Integer.valueOf (j));\r
+            TestHelper.assertEquals (Integer.valueOf (j), lookup);\r
+        }\r
+    }\r
+}\r
diff --git a/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestMultiThreadMapIterator.java b/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestMultiThreadMapIterator.java
new file mode 100644 (file)
index 0000000..91e7b06
--- /dev/null
@@ -0,0 +1,155 @@
+package com.romix.scala.collection.concurrent;\r
+\r
+import java.util.Collection;\r
+import java.util.Iterator;\r
+import java.util.LinkedList;\r
+import java.util.Map;\r
+import java.util.Map.Entry;\r
+import java.util.concurrent.ConcurrentHashMap;\r
+import java.util.concurrent.ExecutorService;\r
+import java.util.concurrent.Executors;\r
+import java.util.concurrent.TimeUnit;\r
+\r
+import org.junit.Test;\r
+\r
+public class TestMultiThreadMapIterator {\r
+    private static final int NTHREADS = 7;\r
+\r
+    @Test\r
+    public void testMultiThreadMapIterator () {\r
+        final Map<Object, Object> bt = new TrieMap<Object, Object> ();\r
+        for (int j = 0; j < 50 * 1000; j++) {\r
+            final Object[] objects = getObjects (j);\r
+            for (final Object o : objects) {\r
+                bt.put (o, o);\r
+            }\r
+        }\r
+\r
+      System.out.println ("Size of initialized map is " + bt.size ());  \r
+      int count = 0;\r
+        {\r
+            final ExecutorService es = Executors.newFixedThreadPool (NTHREADS);\r
+            for (int i = 0; i < NTHREADS; i++) {\r
+                final int threadNo = i;\r
+                es.execute (new Runnable () {\r
+                    @Override\r
+                    public void run () {\r
+                        for (final Iterator<Map.Entry<Object, Object>> i = bt.entrySet ().iterator (); i.hasNext ();) {\r
+                            final Entry<Object, Object> e = i.next ();\r
+                            if (accepts (threadNo, NTHREADS, e.getKey ())) {\r
+                                String newValue = "TEST:" + threadNo; \r
+                                e.setValue (newValue);\r
+                            }\r
+                        }\r
+                    }\r
+                });\r
+            }\r
+\r
+            es.shutdown ();\r
+            try {\r
+                es.awaitTermination (3600L, TimeUnit.SECONDS);\r
+            } catch (final InterruptedException e) {\r
+                e.printStackTrace ();\r
+            }\r
+        }\r
+\r
+        count = 0;\r
+        for (final Map.Entry<Object, Object> kv : bt.entrySet ()) {\r
+            Object value = kv.getValue (); \r
+            TestHelper.assertTrue (value instanceof String);\r
+            count++;\r
+        }\r
+        TestHelper.assertEquals (50000 + 2000 + 1000 + 100, count);\r
+        \r
+        final ConcurrentHashMap<Object, Object> removed = new ConcurrentHashMap<Object, Object> ();\r
+\r
+        {\r
+            final ExecutorService es = Executors.newFixedThreadPool (NTHREADS);\r
+            for (int i = 0; i < NTHREADS; i++) {\r
+                final int threadNo = i;\r
+                es.execute (new Runnable () {\r
+                    @Override\r
+                    public void run () {\r
+                        for (final Iterator<Map.Entry<Object, Object>> i = bt.entrySet ().iterator (); i.hasNext ();) {\r
+                            final Entry<Object, Object> e = i.next ();\r
+                            Object key = e.getKey ();\r
+                            if (accepts (threadNo, NTHREADS, key)) {\r
+                                if (null == bt.get (key)) {\r
+                                    System.out.println (key);\r
+                                }\r
+                                i.remove ();\r
+                                if (null != bt.get (key)) {\r
+                                    System.out.println (key);\r
+                                }\r
+                                removed.put (key, key);\r
+                            }\r
+                        }\r
+                    }\r
+                });\r
+            }\r
+\r
+            es.shutdown ();\r
+            try {\r
+                es.awaitTermination (3600L, TimeUnit.SECONDS);\r
+            } catch (final InterruptedException e) {\r
+                e.printStackTrace ();\r
+            }\r
+        }\r
+\r
+        count = 0;\r
+        for (final Object value : bt.keySet ()) {\r
+            value.toString ();\r
+            count++;\r
+        }\r
+        for (final Object o : bt.keySet ()) {\r
+            if (!removed.contains (bt.get (o))) {\r
+                System.out.println ("Not removed: " + o);\r
+            }\r
+        }\r
+        TestHelper.assertEquals (0, count);\r
+        TestHelper.assertEquals (0, bt.size ());\r
+        TestHelper.assertTrue (bt.isEmpty ());\r
+    }\r
+\r
+    protected static boolean accepts (final int threadNo, final int nThreads, final Object key) {\r
+        int val = getKeyValue (key); \r
+        if(val>=0)\r
+            return val % nThreads == threadNo;\r
+        else\r
+            return false;\r
+    }\r
+\r
+    private static int getKeyValue (final Object key) {\r
+        int val = 0;\r
+        if (key instanceof Integer) {\r
+            val = ((Integer) key).intValue ();\r
+        }\r
+        else if (key instanceof Character) {\r
+            val = Math.abs (Character.getNumericValue ((Character) key) + 1);\r
+        }\r
+        else if (key instanceof Short) {\r
+            val = ((Short) key).intValue () + 2;\r
+        }\r
+        else if (key instanceof Byte) {\r
+            val = ((Byte) key).intValue () + 3;\r
+        } else \r
+            return -1;\r
+        return val;\r
+    }\r
+\r
+    static Object[] getObjects (final int j) {\r
+        final Collection<Object> results = new LinkedList<Object> ();\r
+        results.add (Integer.valueOf (j));\r
+        if (j < 2000) {\r
+            results.add (Character.valueOf ((char) j));\r
+        }\r
+        if (j < 1000) {\r
+            results.add (Short.valueOf ((short) j));\r
+        }\r
+        if (j < 100) {\r
+            results.add (Byte.valueOf ((byte) j));\r
+        }\r
+\r
+        return results.toArray ();\r
+    }\r
+}\r
diff --git a/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestReadOnlyAndUpdatableIterators.java b/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestReadOnlyAndUpdatableIterators.java
new file mode 100644 (file)
index 0000000..7ff5b5d
--- /dev/null
@@ -0,0 +1,130 @@
+package com.romix.scala.collection.concurrent;\r
+\r
+import java.util.Iterator;\r
+import java.util.Map.Entry;\r
+\r
+import org.junit.Before;\r
+import org.junit.Test;\r
+\r
+/***\r
+ * \r
+ * Test that read-only iterators do not allow for any updates.\r
+ * Test that non read-only iterators allow for updates. \r
+ *\r
+ */\r
+public class TestReadOnlyAndUpdatableIterators {\r
+    TrieMap<Integer, Integer> bt;\r
+    private static final int MAP_SIZE = 200;\r
+    \r
+    @Before\r
+    public void setUp() {\r
+        bt = new TrieMap <Integer, Integer> ();\r
+        for (int j = 0; j < MAP_SIZE; j++) {\r
+            TestHelper.assertEquals (null, bt.put (Integer.valueOf (j), Integer.valueOf (j)));\r
+        }                \r
+    }\r
+    \r
+    @Test\r
+    public void testReadOnlyIterator () {\r
+        Iterator<Entry<Integer, Integer>> it = bt.readOnlyIterator ();\r
+        try {\r
+            it.next().setValue (0);\r
+            // It should have generated an exception, because it is a read-only iterator\r
+            TestHelper.assertFalse (true);\r
+        } catch (Exception e) {\r
+            \r
+        }\r
+        try {\r
+            it.remove ();\r
+            // It should have generated an exception, because it is a read-only iterator\r
+            TestHelper.assertFalse (true);\r
+        } catch (Exception e) {\r
+            \r
+        }\r
+    }\r
+\r
+    @Test\r
+    public void testReadOnlySnapshotReadOnlyIterator () {\r
+        TrieMap<Integer, Integer> roSnapshot = bt.readOnlySnapshot ();\r
+        Iterator<Entry<Integer, Integer>> it = roSnapshot.readOnlyIterator ();\r
+        try {\r
+            it.next().setValue (0);\r
+            // It should have generated an exception, because it is a read-only iterator\r
+            TestHelper.assertFalse (true);\r
+        } catch (Exception e) {\r
+            \r
+        }\r
+        try {\r
+            it.remove ();\r
+            // It should have generated an exception, because it is a read-only iterator\r
+            TestHelper.assertFalse (true);\r
+        } catch (Exception e) {\r
+            \r
+        }\r
+    }\r
+\r
+    @Test\r
+    public void testReadOnlySnapshotIterator () {\r
+        TrieMap<Integer, Integer> roSnapshot = bt.readOnlySnapshot ();\r
+        Iterator<Entry<Integer, Integer>> it = roSnapshot.iterator ();\r
+        try {\r
+            it.next().setValue (0);\r
+            // It should have generated an exception, because it is a read-only iterator\r
+            TestHelper.assertFalse (true);\r
+        } catch (Exception e) {\r
+            \r
+        }\r
+        try {\r
+            it.remove ();\r
+            // It should have generated an exception, because it is a read-only iterator\r
+            TestHelper.assertFalse (true);\r
+        } catch (Exception e) {\r
+            \r
+        }\r
+    }\r
+\r
+    @Test\r
+    public void testIterator () {\r
+        Iterator<Entry<Integer, Integer>> it = bt.iterator ();\r
+        try {\r
+            it.next().setValue (0);\r
+        } catch (Exception e) {\r
+            // It should not have generated an exception, because it is a non read-only iterator\r
+            TestHelper.assertFalse (true);            \r
+        }\r
+        \r
+        try {\r
+            it.remove ();\r
+        } catch (Exception e) {\r
+            // It should not have generated an exception, because it is a non read-only iterator\r
+            TestHelper.assertFalse (true);            \r
+        }\r
+        \r
+        // All changes are done on the original map\r
+        TestHelper.assertEquals (MAP_SIZE - 1, bt.size ());            \r
+    }\r
+\r
+    @Test\r
+    public void testSnapshotIterator () {\r
+        TrieMap<Integer, Integer> snapshot = bt.snapshot ();\r
+        Iterator<Entry<Integer, Integer>> it = snapshot.iterator ();\r
+        try {\r
+            it.next().setValue (0);\r
+        } catch (Exception e) {\r
+            // It should not have generated an exception, because it is a non read-only iterator\r
+            TestHelper.assertFalse (true);            \r
+        }\r
+        try {\r
+            it.remove ();\r
+        } catch (Exception e) {\r
+            // It should not have generated an exception, because it is a non read-only iterator\r
+            TestHelper.assertFalse (true);            \r
+        }\r
+\r
+        // All changes are done on the snapshot, not on the original map\r
+        // Map size should remain unchanged\r
+        TestHelper.assertEquals (MAP_SIZE, bt.size ());\r
+        // snapshot size was changed\r
+        TestHelper.assertEquals (MAP_SIZE-1, snapshot.size ());            \r
+    }\r
+}\r
diff --git a/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestSerialization.java b/third-party/triemap/src/test/java/com/romix/scala/collection/concurrent/TestSerialization.java
new file mode 100644 (file)
index 0000000..04a4a2c
--- /dev/null
@@ -0,0 +1,40 @@
+package com.romix.scala.collection.concurrent;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+public class TestSerialization {
+    @Test
+    public void testSerialization() throws IOException, ClassNotFoundException {
+        TrieMap<String, String> map = new TrieMap<String, String>();
+
+        map.put("dude-0", "tom");
+        map.put("dude-1", "john");
+        map.put("dude-3", "ravi");
+        map.put("dude-4", "alex");
+
+        TrieMap<String, String> expected = map.readOnlySnapshot();
+
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        final ObjectOutputStream oos = new ObjectOutputStream(bos);
+        oos.writeObject(expected);
+        oos.close();
+
+        final byte[] bytes = bos.toByteArray();
+        final ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
+        final ObjectInputStream ois = new ObjectInputStream(bis);
+
+        @SuppressWarnings("unchecked")
+        final TrieMap<String, String> actual = (TrieMap<String, String>) ois.readObject();
+        ois.close();
+
+        Assert.assertEquals(expected, actual);
+    }
+}